4 * Citadel/UX setup utility
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 */
40 #define UI_SILENT 3 /* Silent running, for use in scripts */
42 #define SERVICE_NAME "citadel"
43 #define PROTO_NAME "tcp"
46 char setup_directory[128];
49 char *setup_titles[] =
52 "System Administrator",
54 "Name of bit bucket subdirectory",
63 "Enter the full pathname of the directory in which the BBS you are",
64 "creating or updating resides. If you specify a directory other than the",
65 "default, you will need to specify the -h flag to the server when you start",
69 "Enter the name of the system administrator (which is probably you).",
70 "When an account is created with this name, it will automatically be",
71 "assigned the highest access level.",
74 "You should create a user called 'bbs', 'guest', 'citadel', or something",
75 "similar, that will allow users a way into your BBS. The server will run",
76 "under this user ID. Please specify that (numeric) user ID here.",
79 "Select the name of a subdirectory (relative to the main",
80 "Citadel directory - do not type an absolute pathname!) in",
81 "which to place arriving file transfers that otherwise",
85 "Specify the TCP port number on which your server will run. Normally, this",
86 "will be port 504, which is the official port assigned by the IANA for",
87 "Citadel servers. You'll only need to specify a different port number if",
88 "you run multiple BBS's on the same computer and there's something else",
89 "already using port 504.",
119 "Setup has detected that you currently have data files from a Citadel/UX",
120 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
121 "files to version 4.0x format.",
122 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
123 "files, and run setup again.",
129 struct config config;
132 void cleanup(int exitcode)
135 if (setup_type == UI_CURSES) {
142 /* Do an 'init q' if we need to. When we hit the right one, init
143 * will take over and setup won't come back because we didn't do a
144 * fork(). If init isn't found, we fall through the bottom of the
145 * loop and setup exits quietly.
148 execlp("/sbin/init", "init", "q", NULL);
149 execlp("/usr/sbin/init", "init", "q", NULL);
150 execlp("/bin/init", "init", "q", NULL);
151 execlp("/usr/bin/init", "init", "q", NULL);
152 execlp("init", "init", "q", NULL);
158 /* Gets a line from the terminal */
159 /* Where on the screen to start */
160 /* Pointer to string buffer */
161 /* Maximum length - if negative, no-show */
163 void getlin(int yp, int xp, char *string, int lim) {
174 for (a = 0; a < lim; ++a)
178 for (a = 0; a < lim; ++a)
181 printw("%s", string);
182 GLA:move(yp, xp + strlen(string));
190 if ((a == 8) && (strlen(string) == 0))
192 if ((a != 13) && (a != 8) && (strlen(string) == lim))
194 if ((a == 8) && (string[0] != 0)) {
195 string[strlen(string) - 1] = 0;
196 move(yp, xp + strlen(string));
200 if ((a == 13) || (a == 10)) {
203 for (a = 0; a < lim; ++a)
205 mvprintw(yp, xp, "%s", string);
222 void title(char *text)
224 if (setup_type == UI_TEXT) {
225 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
230 void hit_any_key(void)
235 if (setup_type == UI_CURSES) {
236 mvprintw(20, 0, "Press any key to continue... ");
242 if (setup_type == UI_TEXT) {
243 printf("Press return to continue...");
244 fgets(junk, 5, stdin);
248 int yesno(char *question)
253 switch (setup_type) {
257 printf("%s\nYes/No --> ", question);
258 fgets(buf, 4096, stdin);
259 answer = tolower(buf[0]);
262 else if (answer == 'n')
264 } while ((answer < 0) || (answer > 1));
272 mvprintw(1, 20, "Question");
274 mvprintw(10, 0, "%-80s", question);
275 mvprintw(20, 0, "%80s", "");
276 mvprintw(20, 0, "Yes/No -> ");
279 answer = tolower(answer);
282 else if (answer == 'n')
284 } while ((answer < 0) || (answer > 1));
294 void get_setup_msg(char *dispbuf, int msgnum)
300 while (atol(setup_text[a]) != msgnum)
305 strcat(dispbuf, setup_text[a++]);
306 strcat(dispbuf, "\n");
307 } while (atol(setup_text[a]) != (msgnum + 1));
310 void print_setup(int msgnum)
314 get_setup_msg(dispbuf, msgnum);
315 printf("\n\n%s\n\n", dispbuf);
319 void important_message(char *title, char *msgtext)
322 switch (setup_type) {
325 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");
326 printf(" %s \n\n%s\n\n", title, msgtext);
335 printw(" Important Message ");
338 printw("%s", msgtext);
347 void important_msgnum(int msgnum)
351 get_setup_msg(dispbuf, msgnum);
352 important_message("Important Message", dispbuf);
355 void display_error(char *error_message)
357 important_message("Error", error_message);
360 void progress(char *text, long int curr, long int cmax)
362 static long dots_printed;
365 switch (setup_type) {
369 printf("%s\n", text);
370 printf("..........................");
371 printf("..........................");
372 printf("..........................\r");
375 } else if (curr == cmax) {
376 printf("\r%79s\n", "");
378 a = (curr * 100) / cmax;
381 while (dots_printed < a) {
394 printw("%s\n", text);
396 printf("..........................");
397 printf("..........................");
398 printf("..........................\r");
401 } else if (curr == cmax) {
405 a = (curr * 100) / cmax;
410 while (dots_printed < a) {
425 * check_services_entry() -- Make sure "citadel" is in /etc/services
428 void check_services_entry(void)
434 "There is no '%s' entry in /etc/services. Would you like to add one?",
437 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
438 if (yesno(question) == 1) {
439 sfp = fopen("/etc/services", "a");
441 display_error(strerror(errno));
443 fprintf(sfp, "%s 504/tcp\n",
453 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
456 void check_inittab_entry(void)
460 char looking_for[SIZ];
466 /* Determine the fully qualified path name of citserver */
467 sprintf(looking_for, "%s/citserver ", BBSDIR);
469 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
470 * an entry is found which we believe starts citserver.
472 infp = fopen("/etc/inittab", "r");
476 while (fgets(buf, sizeof buf, infp) != NULL) {
477 buf[strlen(buf) - 1] = 0;
478 ptr = strtok(buf, ":");
479 ptr = strtok(NULL, ":");
480 ptr = strtok(NULL, ":");
481 ptr = strtok(NULL, ":");
483 if (!strncmp(ptr, looking_for,
484 strlen(looking_for))) {
492 /* If there's already an entry, then we have nothing left to do. */
496 /* Otherwise, prompt the user to create an entry. */
498 "There is no '%s' entry in /etc/inittab.\n"
499 "Would you like to add one?",
501 if (yesno(question) == 0)
504 /* Generate a unique entry name for /etc/inittab */
505 sprintf(entryname, "c0");
508 if (entryname[1] > '9') {
511 if (entryname[0] > 'z') {
513 "Can't generate a unique entry name");
518 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
519 } while (system(buf) == 0);
521 /* Now write it out to /etc/inittab */
522 infp = fopen("/etc/inittab", "a");
524 display_error(strerror(errno));
526 fprintf(infp, "# Start the Citadel/UX server...\n");
527 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
528 entryname, looking_for, setup_directory);
536 void set_str_val(int msgpos, char str[])
542 strcpy(tempfile, tmpnam(NULL));
543 strcpy(setupmsg, "");
545 switch (setup_type) {
547 title(setup_titles[msgpos]);
549 printf("This is currently set to:\n%s\n", str);
550 printf("Enter new value or press return to leave unchanged:\n");
551 fgets(buf, 4096, stdin);
552 buf[strlen(buf) - 1] = 0;
553 if (strlen(buf) != 0)
559 move(1, ((80 - strlen(setup_titles[msgpos])) / 2));
561 printw("%s", setup_titles[msgpos]);
564 get_setup_msg(setupmsg, msgpos);
565 printw("%s", setupmsg);
567 getlin(20, 0, str, 80);
573 void set_int_val(int msgpos, int *ip)
576 sprintf(buf, "%d", (int) *ip);
577 set_str_val(msgpos, buf);
582 void set_char_val(int msgpos, char *ip)
585 sprintf(buf, "%d", (int) *ip);
586 set_str_val(msgpos, buf);
587 *ip = (char) atoi(buf);
591 void set_long_val(int msgpos, long int *ip)
594 sprintf(buf, "%ld", *ip);
595 set_str_val(msgpos, buf);
600 void edit_value(int curr)
608 set_str_val(curr, config.c_sysadm);
613 set_long_val(curr, &l);
618 set_str_val(curr, config.c_bucket_dir);
619 config.c_bucket_dir[14] = 0;
620 for (a = 0; a < strlen(config.c_bucket_dir); ++a)
621 if (!isalpha(config.c_bucket_dir[a]))
622 strcpy(&config.c_bucket_dir[a],
623 &config.c_bucket_dir[a + 1]);
627 set_int_val(curr, &config.c_port_number);
635 * (re-)write the config data to disk
637 void write_config_to_disk(void)
642 if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
643 display_error("setup: cannot open citadel.config");
646 fp = fdopen(fd, "wb");
648 display_error("setup: cannot open citadel.config");
651 fwrite((char *) &config, sizeof(struct config), 1, fp);
659 * Figure out what type of user interface we're going to use
661 int discover_ui(void)
674 int main(int argc, char *argv[])
680 int old_setup_level = 0;
682 struct utsname my_utsname;
687 /* set an invalid setup type */
690 /* parse command line args */
691 for (a = 0; a < argc; ++a) {
692 if (!strncmp(argv[a], "-u", 2)) {
693 strcpy(aaa, argv[a]);
694 strcpy(aaa, &aaa[2]);
695 setup_type = atoi(aaa);
697 if (!strcmp(argv[a], "-i")) {
700 if (!strcmp(argv[a], "-q")) {
701 setup_type = UI_SILENT;
706 /* If a setup type was not specified, try to determine automatically
707 * the best one to use out of all available types.
709 if (setup_type < 0) {
710 setup_type = discover_ui();
713 if (setup_type == UI_CURSES) {
720 if (info_only == 1) {
721 important_message("Citadel/UX Setup", CITADEL);
724 /* Get started in a valid setup directory. */
725 strcpy(setup_directory, BBSDIR);
726 set_str_val(0, setup_directory);
727 if (chdir(setup_directory) != 0) {
728 important_message("Citadel/UX Setup",
729 "The directory you specified does not exist.");
732 /* Determine our host name, in case we need to use it as a default */
736 switch (setup_type) {
740 " *** Citadel/UX setup program ***\n\n");
746 * What we're going to try to do here is append a whole bunch of
747 * nulls to the citadel.config file, so we can keep the old config
748 * values if they exist, but if the file is missing or from an
749 * earlier version with a shorter config structure, when setup tries
750 * to read the old config parameters, they'll all come up zero.
751 * The length of the config file will be set to what it's supposed
752 * to be when we rewrite it, because we replace the old file with a
753 * completely new copy. (Neat, eh?)
756 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
757 S_IRUSR | S_IWUSR)) == -1) {
758 display_error("setup: cannot append citadel.config");
761 fp = fdopen(a, "ab");
763 display_error("setup: cannot append citadel.config");
766 for (a = 0; a < sizeof(struct config); ++a)
770 /* now we re-open it, and read the old or blank configuration */
771 fp = fopen("citadel.config", "rb");
773 display_error("setup: cannot open citadel.config");
776 fread((char *) &config, sizeof(struct config), 1, fp);
780 /* set some sample/default values in place of blanks... */
781 if (strlen(config.c_nodename) == 0)
782 safestrncpy(config.c_nodename, my_utsname.nodename,
783 sizeof config.c_nodename);
784 strtok(config.c_nodename, ".");
785 if (strlen(config.c_fqdn) == 0) {
786 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
787 safestrncpy(config.c_fqdn, he->h_name,
788 sizeof config.c_fqdn);
790 safestrncpy(config.c_fqdn, my_utsname.nodename,
791 sizeof config.c_fqdn);
793 if (strlen(config.c_humannode) == 0)
794 strcpy(config.c_humannode, "My System");
795 if (strlen(config.c_phonenum) == 0)
796 strcpy(config.c_phonenum, "US 800 555 1212");
797 if (config.c_initax == 0) {
800 if (strlen(config.c_moreprompt) == 0)
801 strcpy(config.c_moreprompt, "<more>");
802 if (strlen(config.c_twitroom) == 0)
803 strcpy(config.c_twitroom, "Trashcan");
804 if (strlen(config.c_bucket_dir) == 0)
805 strcpy(config.c_bucket_dir, "bitbucket");
806 if (strlen(config.c_net_password) == 0)
807 strcpy(config.c_net_password, "netpassword");
808 if (config.c_port_number == 0) {
809 config.c_port_number = 504;
811 if (config.c_ipgm_secret == 0) {
813 config.c_ipgm_secret = rand();
815 if (config.c_sleeping == 0) {
816 config.c_sleeping = 900;
818 if (config.c_bbsuid == 0) {
819 pw = getpwnam("citadel");
821 config.c_bbsuid = pw->pw_uid;
823 if (config.c_bbsuid == 0) {
824 pw = getpwnam("bbs");
826 config.c_bbsuid = pw->pw_uid;
828 if (config.c_bbsuid == 0) {
829 pw = getpwnam("guest");
831 config.c_bbsuid = pw->pw_uid;
833 if (config.c_createax == 0) {
834 config.c_createax = 3;
837 * Negative values for maxsessions are not allowed.
839 if (config.c_maxsessions < 0) {
840 config.c_maxsessions = 0;
842 /* We need a system default message expiry policy, because this is
843 * the top level and there's no 'higher' policy to fall back on.
845 if (config.c_ep.expire_mode == 0) {
846 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
847 config.c_ep.expire_value = 150;
851 * Default port numbers for various services
853 if (config.c_smtp_port == 0) config.c_smtp_port = 25;
854 if (config.c_pop3_port == 0) config.c_pop3_port = 110;
855 if (config.c_imap_port == 0) config.c_imap_port = 143;
858 /* Go through a series of dialogs prompting for config info */
859 if (setup_type != UI_SILENT) {
860 for (curr = 1; curr <= MAXSETUP; ++curr) {
866 if (setuid(config.c_bbsuid) != 0) {
867 important_message("Citadel/UX Setup",
868 "Failed to change the user ID to your BBS user.");
873 /***** begin version update section ***** */
874 /* take care of any updating that is necessary */
876 old_setup_level = config.c_setup_level;
878 if (old_setup_level == 0)
881 if (old_setup_level < 323) {
882 important_message("Citadel/UX Setup",
883 "This Citadel/UX installation is too old "
887 write_config_to_disk();
889 if ((config.c_setup_level / 10) == 32) {
890 important_msgnum(31);
893 if (config.c_setup_level < 400) {
894 config.c_setup_level = 400;
896 /* end of 3.23 -> 4.00 update section */
898 /* end of 4.00 -> 4.02 update section */
900 old_setup_level = config.c_setup_level;
902 /* end of version update section */
905 config.c_setup_level = REV_LEVEL;
907 /******************************************/
909 write_config_to_disk();
913 mkdir("userpics", 0700);
914 mkdir("messages", 0700);
916 mkdir("images", 0700);
917 mkdir("netconfigs", 0700);
918 mkdir(config.c_bucket_dir, 0700);
920 /* Delete a bunch of old files from Citadel v4; don't need anymore */
921 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
923 check_services_entry(); /* Check /etc/services */
924 check_inittab_entry(); /* Check /etc/inittab */
926 if ((pw = getpwuid(config.c_bbsuid)) == NULL)
931 progress("Setting file permissions", 0, 5);
932 chown(".", config.c_bbsuid, gid);
933 progress("Setting file permissions", 1, 5);
934 chown("citadel.config", config.c_bbsuid, gid);
935 progress("Setting file permissions", 2, 5);
937 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
938 (long)config.c_bbsuid, (long)gid);
940 progress("Setting file permissions", 3, 5);
941 chmod("citadel.config", S_IRUSR | S_IWUSR);
942 progress("Setting file permissions", 4, 5);
944 important_message("Setup finished",
945 "Setup is finished. You may now start the Citadel server.");