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));
546 switch (setup_type) {
548 title(setup_titles[msgpos]);
551 dump_access_levels();
552 printf("This is currently set to:\n%s\n", str);
553 printf("Enter new value or press return to leave unchanged:\n");
554 fgets(buf, 4096, stdin);
555 buf[strlen(buf) - 1] = 0;
556 if (strlen(buf) != 0)
562 move(1, ((80 - strlen(setup_titles[msgpos])) / 2));
564 printw("%s", setup_titles[msgpos]);
567 get_setup_msg(setupmsg, msgpos);
568 printw("%s", setupmsg);
570 getlin(20, 0, str, 80);
576 void set_int_val(int msgpos, int *ip)
579 sprintf(buf, "%d", (int) *ip);
580 set_str_val(msgpos, buf);
585 void set_char_val(int msgpos, char *ip)
588 sprintf(buf, "%d", (int) *ip);
589 set_str_val(msgpos, buf);
590 *ip = (char) atoi(buf);
594 void set_long_val(int msgpos, long int *ip)
597 sprintf(buf, "%ld", *ip);
598 set_str_val(msgpos, buf);
603 void edit_value(int curr)
610 set_str_val(curr, config.c_sysadm);
614 set_int_val(curr, &config.c_bbsuid);
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")) {
703 /* If a setup type was not specified, try to determine automatically
704 * the best one to use out of all available types.
706 if (setup_type < 0) {
707 setup_type = discover_ui();
710 if (setup_type == UI_CURSES) {
717 if (info_only == 1) {
718 important_message("Citadel/UX Setup", CITADEL);
721 /* Get started in a valid setup directory. */
722 strcpy(setup_directory, BBSDIR);
723 set_str_val(0, setup_directory);
724 if (chdir(setup_directory) != 0) {
725 important_message("Citadel/UX Setup",
726 "The directory you specified does not exist.");
729 /* Determine our host name, in case we need to use it as a default */
733 switch (setup_type) {
736 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
742 * What we're going to try to do here is append a whole bunch of
743 * nulls to the citadel.config file, so we can keep the old config
744 * values if they exist, but if the file is missing or from an
745 * earlier version with a shorter config structure, when setup tries
746 * to read the old config parameters, they'll all come up zero.
747 * The length of the config file will be set to what it's supposed
748 * to be when we rewrite it, because we replace the old file with a
749 * completely new copy. (Neat, eh?)
752 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
753 S_IRUSR | S_IWUSR)) == -1) {
754 display_error("setup: cannot append citadel.config");
757 fp = fdopen(a, "ab");
759 display_error("setup: cannot append citadel.config");
762 for (a = 0; a < sizeof(struct config); ++a)
766 /* now we re-open it, and read the old or blank configuration */
767 fp = fopen("citadel.config", "rb");
769 display_error("setup: cannot open citadel.config");
772 fread((char *) &config, sizeof(struct config), 1, fp);
776 /* set some sample/default values in place of blanks... */
777 if (strlen(config.c_nodename) == 0)
778 safestrncpy(config.c_nodename, my_utsname.nodename,
779 sizeof config.c_nodename);
780 strtok(config.c_nodename, ".");
781 if (strlen(config.c_fqdn) == 0) {
782 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
783 safestrncpy(config.c_fqdn, he->h_name,
784 sizeof config.c_fqdn);
786 safestrncpy(config.c_fqdn, my_utsname.nodename,
787 sizeof config.c_fqdn);
789 if (strlen(config.c_humannode) == 0)
790 strcpy(config.c_humannode, "My System");
791 if (strlen(config.c_phonenum) == 0)
792 strcpy(config.c_phonenum, "US 800 555 1212");
793 if (config.c_initax == 0)
795 if (strlen(config.c_moreprompt) == 0)
796 strcpy(config.c_moreprompt, "<more>");
797 if (strlen(config.c_twitroom) == 0)
798 strcpy(config.c_twitroom, "Trashcan");
799 if (strlen(config.c_bucket_dir) == 0)
800 strcpy(config.c_bucket_dir, "bitbucket");
801 if (strlen(config.c_net_password) == 0)
802 strcpy(config.c_net_password, "netpassword");
803 if (config.c_port_number == 0) {
804 config.c_port_number = 504;
806 if (config.c_ipgm_secret == 0) {
808 config.c_ipgm_secret = rand();
810 if (config.c_sleeping == 0) {
811 config.c_sleeping = 900;
813 if (config.c_bbsuid == 0) {
814 pw = getpwnam("citadel");
816 config.c_bbsuid = pw->pw_uid;
818 if (config.c_bbsuid == 0) {
819 pw = getpwnam("bbs");
821 config.c_bbsuid = pw->pw_uid;
823 if (config.c_bbsuid == 0) {
824 pw = getpwnam("guest");
826 config.c_bbsuid = pw->pw_uid;
828 if (config.c_createax == 0) {
829 config.c_createax = 3;
832 * Negative values for maxsessions are not allowed.
834 if (config.c_maxsessions < 0) {
835 config.c_maxsessions = 0;
837 /* We need a system default message expiry policy, because this is
838 * the top level and there's no 'higher' policy to fall back on.
840 if (config.c_ep.expire_mode == 0) {
841 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
842 config.c_ep.expire_value = 150;
846 /* Go through a series of dialogs prompting for config info */
847 for (curr = 1; curr <= MAXSETUP; ++curr) {
852 if (setuid(config.c_bbsuid) != 0) {
853 important_message("Citadel/UX Setup",
854 "Failed to change the user ID to your BBS user.");
859 /***** begin version update section ***** */
860 /* take care of any updating that is necessary */
862 old_setup_level = config.c_setup_level;
864 if (old_setup_level == 0)
867 if (old_setup_level < 323) {
868 important_message("Citadel/UX Setup",
869 "This Citadel/UX installation is too old to be upgraded.");
872 write_config_to_disk();
874 if ((config.c_setup_level / 10) == 32) {
875 important_msgnum(31);
878 if (config.c_setup_level < 400) {
879 config.c_setup_level = 400;
881 /* end of 3.23 -> 4.00 update section */
883 /* end of 4.00 -> 4.02 update section */
885 old_setup_level = config.c_setup_level;
887 /* end of version update section */
890 config.c_setup_level = REV_LEVEL;
892 /******************************************/
894 write_config_to_disk();
896 system("mkdir info 2>/dev/null"); /* Create these */
897 system("mkdir bio 2>/dev/null");
898 system("mkdir userpics 2>/dev/null");
899 system("mkdir messages 2>/dev/null");
900 system("mkdir help 2>/dev/null");
901 system("mkdir images 2>/dev/null");
902 sprintf(aaa, "mkdir %s 2>/dev/null", config.c_bucket_dir);
905 /* Delete a bunch of old files from Citadel v4; don't need anymore */
906 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
908 check_services_entry(); /* Check /etc/services */
909 check_inittab_entry(); /* Check /etc/inittab */
911 if ((pw = getpwuid(config.c_bbsuid)) == NULL)
916 progress("Setting file permissions", 0, 5);
917 chown(".", config.c_bbsuid, gid);
918 progress("Setting file permissions", 1, 5);
919 chown("citadel.config", config.c_bbsuid, gid);
920 progress("Setting file permissions", 2, 5);
921 sprintf(aaa, "find . | grep -v chkpwd | xargs chown %d:%d 2>/dev/null",
922 config.c_bbsuid, gid);
924 progress("Setting file permissions", 3, 5);
925 chmod("citadel.config", S_IRUSR | S_IWUSR);
926 progress("Setting file permissions", 4, 5);
928 important_message("Setup finished",
929 "Setup is finished. You may now start the Citadel server.");