2 * Citadel/UX setup program
5 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
14 #include <sys/types.h>
16 #include <sys/utsname.h>
36 #define UI_TEXT 0 /* Default setup type -- text only */
37 #define UI_DIALOG 1 /* Use the 'dialog' program */
38 #define UI_CURSES 2 /* Use curses */
40 #define SERVICE_NAME "citadel"
41 #define PROTO_NAME "tcp"
44 char setup_directory[128];
47 char *setup_titles[] = {
50 "Fully Qualified Domain Name (FQDN)",
51 "Human-readable node name",
54 "System Administrator",
56 "'Room Creator = Room Aide' flag",
57 "Server timeout period",
58 "Initial access level",
59 "Registration requirements",
61 "Twit Detect target room",
62 "Maximum concurrent sessions",
64 "Restrict Internet mail flag",
65 "Name of bit bucket subdirectory",
66 "System net password",
71 char *setup_text[] = {
74 "Enter the full pathname of the directory in which the BBS you are",
75 "creating or updating resides. If you specify a directory other than the",
76 "default, you will need to specify the -h flag to the server when you start",
80 "This is the name your system is known by on a Citadel/UX network. It",
81 "should be 8 characters or less, and should generally be comprised only of",
82 "letters. You can also use numbers and hyphens if necessary.",
85 "This is the name your system is known by on the Internet.",
86 "If you're not on the Internet, simply set this to your",
87 "node name followed by '.local' or something similar.",
90 "This is a longer description of your system, readable by",
91 "us mere humans. It can be up to 20 characters long and it",
92 "can have spaces in it. Note that if you are part of a",
93 "Cit86Net, this is the name your system will be known by on",
97 "This is the main dialup number for your system. If yours",
98 "can not be dialed into, then make one up! It should be in",
99 "the format 'US 000 000 0000' - the US is your country code",
100 "(look it up if you're not in the United States) and the",
101 "rest is, of course, your area code and phone number.",
102 "This doesn't have any use in Citadel/UX, but gateways to",
103 "other networks may require it, and someday we may use this",
104 "to have the networker automatically build a BBS list.",
107 "Enter the geographical location of your system (city and",
108 "state/province/country etc.)",
111 "Enter the name of the system administrator (which is probably you).",
112 "When an account is created with this name, it will automatically be",
113 "assigned the highest access level.",
116 "You should create a user called 'bbs', 'guest', 'citadel', or something",
117 "similar, that will allow users a way into your BBS. The server will run",
118 "under this user ID. Please specify that (numeric) user ID here.",
121 "This is a boolean value. If you set it to 1, anyone who",
122 "creates a class 3 (passworded) or class 4 (invitation",
123 "only) room will automatically become the Room Aide for",
124 "that room, allowing them to edit it, delete/move messages,",
125 "etc. This is an administrative decision: it works well on",
126 "some systems, and not so well on others. Set this to 0 to",
127 "disable this function.",
130 "This setting specifies how long a server session may sit idle before it is",
131 "automatically terminated. The recommended value is 900 seconds (15",
132 "minutes). Note that this has *nothing* to do with any watchdog timer that",
133 "is presented to the user. The server's timeout is intended to kill idle or",
134 "zombie sessions running on a network, etc. ",
135 "You MUST set this to a reasonable value. Setting it to zero will cause",
136 "the server to malfunction.",
139 "This is the access level new users are assigned.",
141 "The most common settings for this will be either 1, for",
142 "systems which require new user validation by the system",
143 "administrator, or 4, for systems which give instant access.",
144 "The current access levels available are:",
147 "'Registration' refers to the boring part of logging into a BBS for the first",
148 "time: typing your name, address, and telephone number. Set this value to 1",
149 "to automatically do registration for new users, or 0 to not auto-register.",
150 "Optionally, you could set it to, say, 2, to auto-register on a user's second",
151 "call, but there really isn't much point to doing this. The recommended",
152 "value is 1 if you've set your inital access level to 1, or 0 if you've set",
153 "your initial access level to something higher.",
156 "Every BBS has its share of problem users. This is one",
157 "good way to deal with them: if you enable this option,",
158 "anyone you flag as a 'problem user' (access level 2) can",
159 "post anywhere they want, but their messages will all be",
160 "automatically moved to a room of your choosing. Set this",
161 "value to 1 to enable Twit Detect, or 0 to disable it.",
164 "This is the name of the room that problem user messages",
165 "get moved to if you have Twit Detect enabled.",
166 "(Note: don't forget to *create* this room!)",
169 "This is the maximum number of concurrent Citadel sessions which may be",
170 "running at any given time. Use this to keep very busy systems from being",
172 " Set this value to 0 to allow an unlimited number of sessions.",
175 "This is the prompt that appears after each screenful of",
176 "text - for users that have chosen that option. Usually",
177 "a simple '<more>' will do, but some folks like to be",
181 "If you have a gateway set up to allow Citadel users to",
182 "send Internet mail, with sendmail, qmail, or whatever, and",
183 "you wish to restrict this to only users to whom you have",
184 "given this privilege, set this flag to 1. Otherwise, set",
185 "it to 0 to allow everyone to send Internet mail.",
186 "(Obviously, if your system doesn't have the ability to",
187 "send mail to the outside world, this is all irrelevant.)",
190 "Select the name of a subdirectory (relative to the main",
191 "Citadel directory - do not type an absolute pathname!) in",
192 "which to place arriving file transfers that otherwise",
193 "don't have a home.",
196 "If you use Citadel client/server sessions to transport network spool data",
197 "between systems, this is the password other systems will use to authenticate",
198 "themselves as network nodes rather than regular callers.",
201 "Specify the TCP port number on which your server will run. Normally, this",
202 "will be port 504, which is the official port assigned by the IANA for",
203 "Citadel servers. You'll only need to specify a different port number if",
204 "you run multiple BBS's on the same computer and there's something else",
205 "already using port 504.",
220 "Setup has detected that you currently have data files from a Citadel/UX",
221 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
222 "files to version 4.0x format.",
223 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
224 "files, and run setup again.",
230 struct config config;
233 void cleanup(int exitcode) {
235 if (setup_type == UI_CURSES) {
242 /* Do an 'init q' if we need to. When we hit the right one, init
243 * will take over and setup won't come back because we didn't do a
244 * fork(). If init isn't found, we fall through the bottom of the
245 * loop and setup exits quietly.
248 execlp("/sbin/init", "init", "q", NULL);
249 execlp("/usr/sbin/init", "init", "q", NULL);
250 execlp("/bin/init", "init", "q", NULL);
251 execlp("/usr/bin/init", "init", "q", NULL);
252 execlp("init", "init", "q", NULL);
260 void getlin(int yp, int xp, char *string, int lim) /* Gets a line from the terminal */
261 /* Where on the screen to start */
262 /* Pointer to string buffer */
263 /* Maximum length - if negative, no-show */
268 if (lim<0) { lim=(0-lim); flag=1; }
271 for (a=0; a<lim; ++a) addch('-');
274 for (a=0; a<lim; ++a) addch(' ');
276 printw("%s", string);
277 GLA: move(yp,xp+strlen(string));
283 if ((a==8)&&(strlen(string)==0)) goto GLA;
284 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
285 if ((a==8)&&(string[0]!=0)) {
286 string[strlen(string)-1]=0;
287 move(yp,xp+strlen(string));
291 if ((a==13)||(a==10)) {
294 for (a=0; a<lim; ++a) addch(' ');
295 mvprintw(yp,xp,"%s",string);
302 if (flag==0) addch(a);
303 if (flag==1) addch('*');
310 void title(char *text)
312 if (setup_type == UI_TEXT) {
313 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
318 void hit_any_key(void) {
322 if (setup_type == UI_CURSES) {
323 mvprintw(20, 0, "Press any key to continue... ");
329 printf("Press return to continue...");
330 fgets(junk, 5, stdin);
333 int yesno(char *question)
342 printf("%s\nYes/No --> ",question);
343 fgets(buf, 4096, stdin);
344 answer=tolower(buf[0]);
345 if (answer=='y') answer=1;
346 else if (answer=='n') answer=0;
347 } while ((answer<0)||(answer>1));
351 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
352 answer = ( (system(buf)==0) ? 1 : 0);
359 mvprintw(1, 20, "Question");
361 mvprintw(10, 0, "%-80s", question);
362 mvprintw(20, 0, "%80s", "");
363 mvprintw(20, 0, "Yes/No -> ");
366 answer=tolower(answer);
367 if (answer=='y') answer=1;
368 else if (answer=='n') answer=0;
369 } while ((answer<0)||(answer>1));
379 void dump_access_levels(void) {
381 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
384 void get_setup_msg(char *dispbuf, int msgnum) {
389 while (atol(setup_text[a]) != msgnum) ++a;
393 strcat(dispbuf, setup_text[a++]);
394 strcat(dispbuf, "\n");
395 } while(atol(setup_text[a])!=(msgnum+1));
398 void print_setup(int msgnum) {
401 get_setup_msg(dispbuf, msgnum);
402 printf("\n\n%s\n\n", dispbuf);
406 void important_message(char *title, char *msgtext) {
412 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");
413 printf(" %s \n\n%s\n\n", title, msgtext);
418 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
427 printw(" Important Message ");
430 printw("%s", msgtext);
439 void important_msgnum(int msgnum) {
442 get_setup_msg(dispbuf, msgnum);
443 important_message("Important Message", dispbuf);
446 void display_error(char *error_message) {
447 important_message("Error", error_message);
450 void progress(char *text, long int curr, long int cmax)
452 static long dots_printed;
455 static FILE *gauge = NULL;
463 printf("..........................");
464 printf("..........................");
465 printf("..........................\r");
469 else if (curr==cmax) {
470 printf("\r%79s\n","");
473 a=(curr * 100) / cmax;
475 while (dots_printed < a) {
490 printf("..........................");
491 printf("..........................");
492 printf("..........................\r");
496 else if (curr==cmax) {
501 a=(curr * 100) / cmax;
505 while (dots_printed < a) {
515 if ( (curr == 0) && (gauge == NULL) ) {
516 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
518 gauge = (FILE *) popen(gcmd, "w");
521 else if (curr==cmax) {
522 fprintf(gauge, "100\n");
527 a=(curr * 100) / cmax;
529 fprintf(gauge, "%ld\n", a);
541 * check_services_entry() -- Make sure "citadel" is in /etc/services
544 void check_services_entry(void) {
549 "There is no '%s' entry in /etc/services. Would you like to add one?",
552 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
553 if (yesno(question)==1) {
554 sfp = fopen("/etc/services", "a");
556 display_error(strerror(errno));
559 fprintf(sfp, "%s 504/tcp\n",
570 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
573 void check_inittab_entry(void) {
576 char looking_for[256];
582 /* Determine the fully qualified path name of citserver */
583 sprintf(looking_for, "%s/citserver ", BBSDIR);
585 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
586 * an entry is found which we believe starts citserver.
588 infp = fopen("/etc/inittab", "r");
590 display_error(strerror(errno));
593 while (fgets(buf, 256, infp) != NULL) {
594 buf[strlen(buf) - 1] = 0;
595 ptr = strtok(buf, ":");
596 ptr = strtok(NULL, ":");
597 ptr = strtok(NULL, ":");
598 ptr = strtok(NULL, ":");
600 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
608 /* If there's already an entry, then we have nothing left to do. */
609 if (have_entry > 0) return;
611 /* Otherwise, prompt the user to create an entry. */
613 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
615 if (yesno(question)==0) return;
617 /* Generate a unique entry name for /etc/inittab */
618 sprintf(entryname, "c0");
621 if (entryname[1] > '9') {
624 if (entryname[0] > 'z') {
626 "Can't generate a unique entry name");
631 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
632 } while(system(buf)==0);
634 /* Now write it out to /etc/inittab */
635 infp = fopen("/etc/inittab", "a");
637 display_error(strerror(errno));
640 fprintf(infp, "# Start the Citadel/UX server...\n");
641 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
642 entryname, looking_for, setup_directory);
650 void set_str_val(int msgpos, char str[]) {
656 sprintf(tempfile, "/tmp/setup.%d", getpid());
658 switch (setup_type) {
660 title(setup_titles[msgpos]);
662 if (msgpos==11) dump_access_levels();
663 printf("This is currently set to:\n%s\n",str);
664 printf("Enter new value or press return to leave unchanged:\n");
665 fgets(buf, 4096, stdin);
666 buf[strlen(buf)-1] = 0;
667 if (strlen(buf)!=0) strcpy(str,buf);
670 get_setup_msg(setupmsg, msgpos);
672 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
673 setup_titles[msgpos],
676 if (system(buf)==0) {
677 fp = fopen(tempfile, "rb");
678 fgets(str, 4095, fp);
681 if (str[strlen(str)-1]==10)
682 str[strlen(str)-1]=0;
688 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
690 printw("%s", setup_titles[msgpos]);
693 get_setup_msg(setupmsg, msgpos);
694 printw("%s", setupmsg);
696 getlin(20, 0, str, 80);
702 void set_int_val(int msgpos, int *ip)
705 sprintf(buf,"%d",(int)*ip);
706 set_str_val(msgpos, buf);
711 void set_char_val(int msgpos, char *ip)
714 sprintf(buf,"%d",(int)*ip);
715 set_str_val(msgpos, buf);
716 *ip = (char)atoi(buf);
720 void set_long_val(int msgpos, long int *ip)
723 sprintf(buf,"%ld",*ip);
724 set_str_val(msgpos, buf);
729 void edit_value(int curr)
736 set_str_val(curr, config.c_nodename);
740 set_str_val(curr, config.c_fqdn);
744 set_str_val(curr, config.c_humannode);
748 set_str_val(curr, config.c_phonenum);
752 set_str_val(curr, config.c_bbs_city);
756 set_str_val(curr, config.c_sysadm);
760 set_int_val(curr, &config.c_bbsuid);
764 set_char_val(curr, &config.c_creataide);
768 set_int_val(curr, &config.c_sleeping);
772 set_char_val(curr, &config.c_initax);
776 set_char_val(curr, &config.c_regiscall);
780 set_char_val(curr, &config.c_twitdetect);
784 set_str_val(curr, config.c_twitroom);
788 set_int_val(curr, &config.c_maxsessions);
792 set_str_val(curr, config.c_moreprompt);
796 set_char_val(curr, &config.c_restrict);
800 set_str_val(curr, config.c_bucket_dir);
801 config.c_bucket_dir[14] = 0;
802 for (a=0; a<strlen(config.c_bucket_dir); ++a)
803 if (!isalpha(config.c_bucket_dir[a]))
804 strcpy(&config.c_bucket_dir[a],
805 &config.c_bucket_dir[a+1]);
809 set_str_val(curr, config.c_net_password);
813 set_int_val(curr, &config.c_port_number);
821 * (re-)write the config data to disk
823 void write_config_to_disk(void) {
826 fp=fopen("citadel.config","wb");
828 display_error("setup: cannot open citadel.config");
831 fwrite((char *)&config,sizeof(struct config),1,fp);
839 * Figure out what type of user interface we're going to use
841 int discover_ui(void) {
847 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
858 int main(int argc, char *argv[]) {
863 int old_setup_level = 0;
865 struct utsname my_utsname;
868 /* set an invalid setup type */
871 /* parse command line args */
872 for (a=0; a<argc; ++a) {
873 if (!strncmp(argv[a], "-u", 2)) {
874 strcpy(aaa, argv[a]);
875 strcpy(aaa, &aaa[2]);
876 setup_type = atoi(aaa);
878 if (!strcmp(argv[a], "-i")) {
884 /* If a setup type was not specified, try to determine automatically
885 * the best one to use out of all available types.
887 if (setup_type < 0) {
888 setup_type = discover_ui();
892 if (setup_type == UI_CURSES) {
899 if (info_only == 1) {
900 important_message("Citadel/UX Setup", CITADEL);
904 /* Get started in a valid setup directory. */
905 strcpy(setup_directory, BBSDIR);
906 set_str_val(0, setup_directory);
907 if (chdir(setup_directory) != 0) {
908 important_message("Citadel/UX Setup",
909 "The directory you specified does not exist.");
913 /* Determine our host name, in case we need to use it as a default */
920 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
924 system("exec clear");
930 * What we're going to try to do here is append a whole bunch of
931 * nulls to the citadel.config file, so we can keep the old config
932 * values if they exist, but if the file is missing or from an
933 * earlier version with a shorter config structure, when setup tries
934 * to read the old config parameters, they'll all come up zero.
935 * The length of the config file will be set to what it's supposed
936 * to be when we rewrite it, because we replace the old file with a
937 * completely new copy. (Neat, eh?)
940 fp=fopen("citadel.config","ab");
942 display_error("setup: cannot append citadel.config");
945 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
948 /* now we re-open it, and read the old or blank configuration */
949 fp=fopen("citadel.config","rb");
951 display_error("setup: cannot open citadel.config");
954 fread((char *)&config,sizeof(struct config),1,fp);
958 /* set some sample/default values in place of blanks... */
959 if (strlen(config.c_nodename)==0)
960 strcpy(config.c_nodename, my_utsname.nodename);
961 if (strlen(config.c_fqdn)==0)
962 sprintf(config.c_fqdn, "%s.local", my_utsname.nodename);
963 if (strlen(config.c_humannode)==0)
964 strcpy(config.c_humannode,"My System");
965 if (strlen(config.c_phonenum)==0)
966 strcpy(config.c_phonenum,"US 800 555 1212");
967 if (config.c_initax == 0)
969 if (strlen(config.c_moreprompt)==0)
970 strcpy(config.c_moreprompt,"<more>");
971 if (strlen(config.c_twitroom)==0)
972 strcpy(config.c_twitroom,"Trashcan");
973 if (strlen(config.c_bucket_dir)==0)
974 strcpy(config.c_bucket_dir,"bitbucket");
975 if (strlen(config.c_net_password)==0)
976 strcpy(config.c_net_password,"netpassword");
977 if (config.c_port_number == 0) {
978 config.c_port_number = 504;
980 if (config.c_ipgm_secret == 0) {
982 config.c_ipgm_secret = rand();
984 if (config.c_sleeping == 0) {
985 config.c_sleeping = 900;
987 if (config.c_bbsuid == 0) {
988 pw = getpwnam("citadel");
989 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
991 if (config.c_bbsuid == 0) {
992 pw = getpwnam("bbs");
993 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
995 if (config.c_bbsuid == 0) {
996 pw = getpwnam("guest");
997 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
1000 /* We need a system default message expiry policy, because this is
1001 * the top level and there's no 'higher' policy to fall back on.
1003 if (config.c_ep.expire_mode == 0) {
1004 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
1005 config.c_ep.expire_value = 150;
1008 /* Go through a series of dialogs prompting for config info */
1009 for (curr = 1; curr <= MAXSETUP; ++curr) {
1014 if (setuid(config.c_bbsuid) != 0) {
1015 important_message("Citadel/UX Setup",
1016 "Failed to change the user ID to your BBS user.");
1021 /***** begin version update section ***** */
1022 /* take care of any updating that is necessary */
1024 old_setup_level = config.c_setup_level;
1026 if (old_setup_level == 0) goto NEW_INST;
1028 if (old_setup_level < 323) {
1029 important_message("Citadel/UX Setup",
1030 "This Citadel/UX installation is too old to be upgraded.");
1034 write_config_to_disk();
1036 if ((config.c_setup_level / 10) == 32) {
1037 important_msgnum(31);
1041 if (config.c_setup_level < 400) {
1042 config.c_setup_level = 400;
1045 /* end of 3.23 -> 4.00 update section */
1047 /* end of 4.00 -> 4.02 update section */
1049 old_setup_level = config.c_setup_level;
1051 /* end of version update section */
1054 config.c_setup_level = REV_LEVEL;
1056 /******************************************/
1058 write_config_to_disk();
1060 system("mkdir info 2>/dev/null"); /* Create these */
1061 system("mkdir bio 2>/dev/null");
1062 system("mkdir userpics 2>/dev/null");
1063 system("mkdir messages 2>/dev/null");
1064 system("mkdir help 2>/dev/null");
1065 system("mkdir images 2>/dev/null");
1066 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1069 /* Delete a bunch of old files from Citadel v4; don't need anymore */
1070 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
1072 check_services_entry(); /* Check /etc/services */
1073 check_inittab_entry(); /* Check /etc/inittab */
1075 progress("Setting file permissions", 0, 3);
1076 chown(".", config.c_bbsuid, getgid());
1077 progress("Setting file permissions", 1, 3);
1078 chown("citadel.config", config.c_bbsuid, getgid());
1079 progress("Setting file permissions", 2, 3);
1080 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1083 progress("Setting file permissions", 3, 3);
1085 important_message("Setup finished",
1086 "Setup is finished. You may now start the Citadel server.");