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 */
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)
612 set_str_val(curr, config.c_sysadm);
617 set_long_val(curr, &l);
622 set_str_val(curr, config.c_bucket_dir);
623 config.c_bucket_dir[14] = 0;
624 for (a = 0; a < strlen(config.c_bucket_dir); ++a)
625 if (!isalpha(config.c_bucket_dir[a]))
626 strcpy(&config.c_bucket_dir[a],
627 &config.c_bucket_dir[a + 1]);
631 set_int_val(curr, &config.c_port_number);
639 * (re-)write the config data to disk
641 void write_config_to_disk(void)
646 if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
647 display_error("setup: cannot open citadel.config");
650 fp = fdopen(fd, "wb");
652 display_error("setup: cannot open citadel.config");
655 fwrite((char *) &config, sizeof(struct config), 1, fp);
663 * Figure out what type of user interface we're going to use
665 int discover_ui(void)
678 int main(int argc, char *argv[])
684 int old_setup_level = 0;
686 struct utsname my_utsname;
691 /* set an invalid setup type */
694 /* parse command line args */
695 for (a = 0; a < argc; ++a) {
696 if (!strncmp(argv[a], "-u", 2)) {
697 strcpy(aaa, argv[a]);
698 strcpy(aaa, &aaa[2]);
699 setup_type = atoi(aaa);
701 if (!strcmp(argv[a], "-i")) {
707 /* If a setup type was not specified, try to determine automatically
708 * the best one to use out of all available types.
710 if (setup_type < 0) {
711 setup_type = discover_ui();
714 if (setup_type == UI_CURSES) {
721 if (info_only == 1) {
722 important_message("Citadel/UX Setup", CITADEL);
725 /* Get started in a valid setup directory. */
726 strcpy(setup_directory, BBSDIR);
727 set_str_val(0, setup_directory);
728 if (chdir(setup_directory) != 0) {
729 important_message("Citadel/UX Setup",
730 "The directory you specified does not exist.");
733 /* Determine our host name, in case we need to use it as a default */
737 switch (setup_type) {
740 printf("\n\n\n *** 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)
799 if (strlen(config.c_moreprompt) == 0)
800 strcpy(config.c_moreprompt, "<more>");
801 if (strlen(config.c_twitroom) == 0)
802 strcpy(config.c_twitroom, "Trashcan");
803 if (strlen(config.c_bucket_dir) == 0)
804 strcpy(config.c_bucket_dir, "bitbucket");
805 if (strlen(config.c_net_password) == 0)
806 strcpy(config.c_net_password, "netpassword");
807 if (config.c_port_number == 0) {
808 config.c_port_number = 504;
810 if (config.c_ipgm_secret == 0) {
812 config.c_ipgm_secret = rand();
814 if (config.c_sleeping == 0) {
815 config.c_sleeping = 900;
817 if (config.c_bbsuid == 0) {
818 pw = getpwnam("citadel");
820 config.c_bbsuid = pw->pw_uid;
822 if (config.c_bbsuid == 0) {
823 pw = getpwnam("bbs");
825 config.c_bbsuid = pw->pw_uid;
827 if (config.c_bbsuid == 0) {
828 pw = getpwnam("guest");
830 config.c_bbsuid = pw->pw_uid;
832 if (config.c_createax == 0) {
833 config.c_createax = 3;
836 * Negative values for maxsessions are not allowed.
838 if (config.c_maxsessions < 0) {
839 config.c_maxsessions = 0;
841 /* We need a system default message expiry policy, because this is
842 * the top level and there's no 'higher' policy to fall back on.
844 if (config.c_ep.expire_mode == 0) {
845 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
846 config.c_ep.expire_value = 150;
850 * Default port numbers for various services
852 if (config.c_pop3_port == 0) config.c_pop3_port = 110;
853 if (config.c_smtp_port == 0) config.c_smtp_port = 25;
856 /* Go through a series of dialogs prompting for config info */
857 for (curr = 1; curr <= MAXSETUP; ++curr) {
862 if (setuid(config.c_bbsuid) != 0) {
863 important_message("Citadel/UX Setup",
864 "Failed to change the user ID to your BBS user.");
869 /***** begin version update section ***** */
870 /* take care of any updating that is necessary */
872 old_setup_level = config.c_setup_level;
874 if (old_setup_level == 0)
877 if (old_setup_level < 323) {
878 important_message("Citadel/UX Setup",
879 "This Citadel/UX installation is too old to be upgraded.");
882 write_config_to_disk();
884 if ((config.c_setup_level / 10) == 32) {
885 important_msgnum(31);
888 if (config.c_setup_level < 400) {
889 config.c_setup_level = 400;
891 /* end of 3.23 -> 4.00 update section */
893 /* end of 4.00 -> 4.02 update section */
895 old_setup_level = config.c_setup_level;
897 /* end of version update section */
900 config.c_setup_level = REV_LEVEL;
902 /******************************************/
904 write_config_to_disk();
906 system("mkdir info 2>/dev/null"); /* Create these */
907 system("mkdir bio 2>/dev/null");
908 system("mkdir userpics 2>/dev/null");
909 system("mkdir messages 2>/dev/null");
910 system("mkdir help 2>/dev/null");
911 system("mkdir images 2>/dev/null");
912 sprintf(aaa, "mkdir %s 2>/dev/null", config.c_bucket_dir);
915 /* Delete a bunch of old files from Citadel v4; don't need anymore */
916 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
918 check_services_entry(); /* Check /etc/services */
919 check_inittab_entry(); /* Check /etc/inittab */
921 if ((pw = getpwuid(config.c_bbsuid)) == NULL)
926 progress("Setting file permissions", 0, 5);
927 chown(".", config.c_bbsuid, gid);
928 progress("Setting file permissions", 1, 5);
929 chown("citadel.config", config.c_bbsuid, gid);
930 progress("Setting file permissions", 2, 5);
931 sprintf(aaa, "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
932 (long)config.c_bbsuid, (long)gid);
934 progress("Setting file permissions", 3, 5);
935 chmod("citadel.config", S_IRUSR | S_IWUSR);
936 progress("Setting file permissions", 4, 5);
938 important_message("Setup finished",
939 "Setup is finished. You may now start the Citadel server.");