2 * Citadel/UX setup program
5 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
14 #include <sys/types.h>
16 #include <sys/utsname.h>
37 #define UI_TEXT 0 /* Default setup type -- text only */
38 #define UI_DIALOG 1 /* Use the 'dialog' program (REMOVED) */
39 #define UI_CURSES 2 /* Use curses */
41 #define SERVICE_NAME "citadel"
42 #define PROTO_NAME "tcp"
45 char setup_directory[128];
48 char *setup_titles[] =
51 "System Administrator",
53 "Name of bit bucket subdirectory",
62 "Enter the full pathname of the directory in which the BBS you are",
63 "creating or updating resides. If you specify a directory other than the",
64 "default, you will need to specify the -h flag to the server when you start",
68 "Enter the name of the system administrator (which is probably you).",
69 "When an account is created with this name, it will automatically be",
70 "assigned the highest access level.",
73 "You should create a user called 'bbs', 'guest', 'citadel', or something",
74 "similar, that will allow users a way into your BBS. The server will run",
75 "under this user ID. Please specify that (numeric) user ID here.",
78 "Select the name of a subdirectory (relative to the main",
79 "Citadel directory - do not type an absolute pathname!) in",
80 "which to place arriving file transfers that otherwise",
84 "Specify the TCP port number on which your server will run. Normally, this",
85 "will be port 504, which is the official port assigned by the IANA for",
86 "Citadel servers. You'll only need to specify a different port number if",
87 "you run multiple BBS's on the same computer and there's something else",
88 "already using port 504.",
118 "Setup has detected that you currently have data files from a Citadel/UX",
119 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
120 "files to version 4.0x format.",
121 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
122 "files, and run setup again.",
128 struct config config;
131 void cleanup(int exitcode)
134 if (setup_type == UI_CURSES) {
141 /* Do an 'init q' if we need to. When we hit the right one, init
142 * will take over and setup won't come back because we didn't do a
143 * fork(). If init isn't found, we fall through the bottom of the
144 * loop and setup exits quietly.
147 execlp("/sbin/init", "init", "q", NULL);
148 execlp("/usr/sbin/init", "init", "q", NULL);
149 execlp("/bin/init", "init", "q", NULL);
150 execlp("/usr/bin/init", "init", "q", NULL);
151 execlp("init", "init", "q", NULL);
157 /* Gets a line from the terminal */
158 /* Where on the screen to start */
159 /* Pointer to string buffer */
160 /* Maximum length - if negative, no-show */
162 void getlin(int yp, int xp, char *string, int lim) {
173 for (a = 0; a < lim; ++a)
177 for (a = 0; a < lim; ++a)
180 printw("%s", string);
181 GLA:move(yp, xp + strlen(string));
189 if ((a == 8) && (strlen(string) == 0))
191 if ((a != 13) && (a != 8) && (strlen(string) == lim))
193 if ((a == 8) && (string[0] != 0)) {
194 string[strlen(string) - 1] = 0;
195 move(yp, xp + strlen(string));
199 if ((a == 13) || (a == 10)) {
202 for (a = 0; a < lim; ++a)
204 mvprintw(yp, xp, "%s", string);
221 void title(char *text)
223 if (setup_type == UI_TEXT) {
224 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
229 void hit_any_key(void)
234 if (setup_type == UI_CURSES) {
235 mvprintw(20, 0, "Press any key to continue... ");
241 printf("Press return to continue...");
242 fgets(junk, 5, stdin);
245 int yesno(char *question)
250 switch (setup_type) {
254 printf("%s\nYes/No --> ", question);
255 fgets(buf, 4096, stdin);
256 answer = tolower(buf[0]);
259 else if (answer == 'n')
261 } while ((answer < 0) || (answer > 1));
269 mvprintw(1, 20, "Question");
271 mvprintw(10, 0, "%-80s", question);
272 mvprintw(20, 0, "%80s", "");
273 mvprintw(20, 0, "Yes/No -> ");
276 answer = tolower(answer);
279 else if (answer == 'n')
281 } while ((answer < 0) || (answer > 1));
291 void dump_access_levels(void)
294 for (a = 0; a <= 6; ++a)
295 printf("%d %s\n", a, axdefs[a]);
298 void get_setup_msg(char *dispbuf, int msgnum)
304 while (atol(setup_text[a]) != msgnum)
309 strcat(dispbuf, setup_text[a++]);
310 strcat(dispbuf, "\n");
311 } while (atol(setup_text[a]) != (msgnum + 1));
314 void print_setup(int msgnum)
318 get_setup_msg(dispbuf, msgnum);
319 printf("\n\n%s\n\n", dispbuf);
323 void important_message(char *title, char *msgtext)
326 switch (setup_type) {
329 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
330 printf(" %s \n\n%s\n\n", title, msgtext);
339 printw(" Important Message ");
342 printw("%s", msgtext);
351 void important_msgnum(int msgnum)
355 get_setup_msg(dispbuf, msgnum);
356 important_message("Important Message", dispbuf);
359 void display_error(char *error_message)
361 important_message("Error", error_message);
364 void progress(char *text, long int curr, long int cmax)
366 static long dots_printed;
369 switch (setup_type) {
373 printf("%s\n", text);
374 printf("..........................");
375 printf("..........................");
376 printf("..........................\r");
379 } else if (curr == cmax) {
380 printf("\r%79s\n", "");
382 a = (curr * 100) / cmax;
385 while (dots_printed < a) {
398 printw("%s\n", text);
400 printf("..........................");
401 printf("..........................");
402 printf("..........................\r");
405 } else if (curr == cmax) {
409 a = (curr * 100) / cmax;
414 while (dots_printed < a) {
429 * check_services_entry() -- Make sure "citadel" is in /etc/services
432 void check_services_entry(void)
438 "There is no '%s' entry in /etc/services. Would you like to add one?",
441 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
442 if (yesno(question) == 1) {
443 sfp = fopen("/etc/services", "a");
445 display_error(strerror(errno));
447 fprintf(sfp, "%s 504/tcp\n",
457 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
460 void check_inittab_entry(void)
464 char looking_for[256];
470 /* Determine the fully qualified path name of citserver */
471 sprintf(looking_for, "%s/citserver ", BBSDIR);
473 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
474 * an entry is found which we believe starts citserver.
476 infp = fopen("/etc/inittab", "r");
480 while (fgets(buf, 256, infp) != NULL) {
481 buf[strlen(buf) - 1] = 0;
482 ptr = strtok(buf, ":");
483 ptr = strtok(NULL, ":");
484 ptr = strtok(NULL, ":");
485 ptr = strtok(NULL, ":");
487 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
495 /* If there's already an entry, then we have nothing left to do. */
499 /* Otherwise, prompt the user to create an entry. */
501 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
503 if (yesno(question) == 0)
506 /* Generate a unique entry name for /etc/inittab */
507 sprintf(entryname, "c0");
510 if (entryname[1] > '9') {
513 if (entryname[0] > 'z') {
515 "Can't generate a unique entry name");
520 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
521 } while (system(buf) == 0);
523 /* Now write it out to /etc/inittab */
524 infp = fopen("/etc/inittab", "a");
526 display_error(strerror(errno));
528 fprintf(infp, "# Start the Citadel/UX server...\n");
529 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
530 entryname, looking_for, setup_directory);
538 void set_str_val(int msgpos, char str[])
544 sprintf(tempfile, tmpnam(NULL));
545 strcpy(setupmsg, "");
547 switch (setup_type) {
549 title(setup_titles[msgpos]);
552 dump_access_levels();
553 printf("This is currently set to:\n%s\n", str);
554 printf("Enter new value or press return to leave unchanged:\n");
555 fgets(buf, 4096, stdin);
556 buf[strlen(buf) - 1] = 0;
557 if (strlen(buf) != 0)
563 move(1, ((80 - strlen(setup_titles[msgpos])) / 2));
565 printw("%s", setup_titles[msgpos]);
568 get_setup_msg(setupmsg, msgpos);
569 printw("%s", setupmsg);
571 getlin(20, 0, str, 80);
577 void set_int_val(int msgpos, int *ip)
580 sprintf(buf, "%d", (int) *ip);
581 set_str_val(msgpos, buf);
586 void set_char_val(int msgpos, char *ip)
589 sprintf(buf, "%d", (int) *ip);
590 set_str_val(msgpos, buf);
591 *ip = (char) atoi(buf);
595 void set_long_val(int msgpos, long int *ip)
598 sprintf(buf, "%ld", *ip);
599 set_str_val(msgpos, buf);
604 void edit_value(int curr)
611 set_str_val(curr, config.c_sysadm);
615 set_int_val(curr, &config.c_bbsuid);
619 set_str_val(curr, config.c_bucket_dir);
620 config.c_bucket_dir[14] = 0;
621 for (a = 0; a < strlen(config.c_bucket_dir); ++a)
622 if (!isalpha(config.c_bucket_dir[a]))
623 strcpy(&config.c_bucket_dir[a],
624 &config.c_bucket_dir[a + 1]);
628 set_int_val(curr, &config.c_port_number);
636 * (re-)write the config data to disk
638 void write_config_to_disk(void)
643 if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
644 display_error("setup: cannot open citadel.config");
647 fp = fdopen(fd, "wb");
649 display_error("setup: cannot open citadel.config");
652 fwrite((char *) &config, sizeof(struct config), 1, fp);
660 * Figure out what type of user interface we're going to use
662 int discover_ui(void)
675 int main(int argc, char *argv[])
681 int old_setup_level = 0;
683 struct utsname my_utsname;
688 /* set an invalid setup type */
691 /* parse command line args */
692 for (a = 0; a < argc; ++a) {
693 if (!strncmp(argv[a], "-u", 2)) {
694 strcpy(aaa, argv[a]);
695 strcpy(aaa, &aaa[2]);
696 setup_type = atoi(aaa);
698 if (!strcmp(argv[a], "-i")) {
704 /* If a setup type was not specified, try to determine automatically
705 * the best one to use out of all available types.
707 if (setup_type < 0) {
708 setup_type = discover_ui();
711 if (setup_type == UI_CURSES) {
718 if (info_only == 1) {
719 important_message("Citadel/UX Setup", CITADEL);
722 /* Get started in a valid setup directory. */
723 strcpy(setup_directory, BBSDIR);
724 set_str_val(0, setup_directory);
725 if (chdir(setup_directory) != 0) {
726 important_message("Citadel/UX Setup",
727 "The directory you specified does not exist.");
730 /* Determine our host name, in case we need to use it as a default */
734 switch (setup_type) {
737 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
743 * What we're going to try to do here is append a whole bunch of
744 * nulls to the citadel.config file, so we can keep the old config
745 * values if they exist, but if the file is missing or from an
746 * earlier version with a shorter config structure, when setup tries
747 * to read the old config parameters, they'll all come up zero.
748 * The length of the config file will be set to what it's supposed
749 * to be when we rewrite it, because we replace the old file with a
750 * completely new copy. (Neat, eh?)
753 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
754 S_IRUSR | S_IWUSR)) == -1) {
755 display_error("setup: cannot append citadel.config");
758 fp = fdopen(a, "ab");
760 display_error("setup: cannot append citadel.config");
763 for (a = 0; a < sizeof(struct config); ++a)
767 /* now we re-open it, and read the old or blank configuration */
768 fp = fopen("citadel.config", "rb");
770 display_error("setup: cannot open citadel.config");
773 fread((char *) &config, sizeof(struct config), 1, fp);
777 /* set some sample/default values in place of blanks... */
778 if (strlen(config.c_nodename) == 0)
779 safestrncpy(config.c_nodename, my_utsname.nodename,
780 sizeof config.c_nodename);
781 strtok(config.c_nodename, ".");
782 if (strlen(config.c_fqdn) == 0) {
783 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
784 safestrncpy(config.c_fqdn, he->h_name,
785 sizeof config.c_fqdn);
787 safestrncpy(config.c_fqdn, my_utsname.nodename,
788 sizeof config.c_fqdn);
790 if (strlen(config.c_humannode) == 0)
791 strcpy(config.c_humannode, "My System");
792 if (strlen(config.c_phonenum) == 0)
793 strcpy(config.c_phonenum, "US 800 555 1212");
794 if (config.c_initax == 0)
796 if (strlen(config.c_moreprompt) == 0)
797 strcpy(config.c_moreprompt, "<more>");
798 if (strlen(config.c_twitroom) == 0)
799 strcpy(config.c_twitroom, "Trashcan");
800 if (strlen(config.c_bucket_dir) == 0)
801 strcpy(config.c_bucket_dir, "bitbucket");
802 if (strlen(config.c_net_password) == 0)
803 strcpy(config.c_net_password, "netpassword");
804 if (config.c_port_number == 0) {
805 config.c_port_number = 504;
807 if (config.c_ipgm_secret == 0) {
809 config.c_ipgm_secret = rand();
811 if (config.c_sleeping == 0) {
812 config.c_sleeping = 900;
814 if (config.c_bbsuid == 0) {
815 pw = getpwnam("citadel");
817 config.c_bbsuid = pw->pw_uid;
819 if (config.c_bbsuid == 0) {
820 pw = getpwnam("bbs");
822 config.c_bbsuid = pw->pw_uid;
824 if (config.c_bbsuid == 0) {
825 pw = getpwnam("guest");
827 config.c_bbsuid = pw->pw_uid;
829 if (config.c_createax == 0) {
830 config.c_createax = 3;
833 * Negative values for maxsessions are not allowed.
835 if (config.c_maxsessions < 0) {
836 config.c_maxsessions = 0;
838 /* We need a system default message expiry policy, because this is
839 * the top level and there's no 'higher' policy to fall back on.
841 if (config.c_ep.expire_mode == 0) {
842 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
843 config.c_ep.expire_value = 150;
847 * Default port numbers for various services
849 if (config.c_pop3_port == 0) config.c_pop3_port = 110;
850 if (config.c_smtp_port == 0) config.c_smtp_port = 25;
853 /* Go through a series of dialogs prompting for config info */
854 for (curr = 1; curr <= MAXSETUP; ++curr) {
859 if (setuid(config.c_bbsuid) != 0) {
860 important_message("Citadel/UX Setup",
861 "Failed to change the user ID to your BBS user.");
866 /***** begin version update section ***** */
867 /* take care of any updating that is necessary */
869 old_setup_level = config.c_setup_level;
871 if (old_setup_level == 0)
874 if (old_setup_level < 323) {
875 important_message("Citadel/UX Setup",
876 "This Citadel/UX installation is too old to be upgraded.");
879 write_config_to_disk();
881 if ((config.c_setup_level / 10) == 32) {
882 important_msgnum(31);
885 if (config.c_setup_level < 400) {
886 config.c_setup_level = 400;
888 /* end of 3.23 -> 4.00 update section */
890 /* end of 4.00 -> 4.02 update section */
892 old_setup_level = config.c_setup_level;
894 /* end of version update section */
897 config.c_setup_level = REV_LEVEL;
899 /******************************************/
901 write_config_to_disk();
903 system("mkdir info 2>/dev/null"); /* Create these */
904 system("mkdir bio 2>/dev/null");
905 system("mkdir userpics 2>/dev/null");
906 system("mkdir messages 2>/dev/null");
907 system("mkdir help 2>/dev/null");
908 system("mkdir images 2>/dev/null");
909 sprintf(aaa, "mkdir %s 2>/dev/null", config.c_bucket_dir);
912 /* Delete a bunch of old files from Citadel v4; don't need anymore */
913 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
915 check_services_entry(); /* Check /etc/services */
916 check_inittab_entry(); /* Check /etc/inittab */
918 if ((pw = getpwuid(config.c_bbsuid)) == NULL)
923 progress("Setting file permissions", 0, 5);
924 chown(".", config.c_bbsuid, gid);
925 progress("Setting file permissions", 1, 5);
926 chown("citadel.config", config.c_bbsuid, gid);
927 progress("Setting file permissions", 2, 5);
928 sprintf(aaa, "find . | grep -v chkpwd | xargs chown %d:%d 2>/dev/null",
929 config.c_bbsuid, gid);
931 progress("Setting file permissions", 3, 5);
932 chmod("citadel.config", S_IRUSR | S_IWUSR);
933 progress("Setting file permissions", 4, 5);
935 important_message("Setup finished",
936 "Setup is finished. You may now start the Citadel server.");