2 * Citadel/UX setup program
4 * see copyright.txt for copyright information
6 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
15 #include <sys/types.h>
33 #define UI_TEXT 0 /* Default setup type -- text only */
34 #define UI_DIALOG 1 /* Use the 'dialog' program */
35 #define UI_CURSES 2 /* Use curses */
37 #define SERVICE_NAME "citadel"
38 #define PROTO_NAME "tcp"
41 char setup_directory[128];
44 char *setup_titles[] = {
47 "Fully Qualified Domain Name (FQDN)",
48 "Human-readable node name",
51 "System Administrator",
53 "Password encryption seed",
54 "'Room Creator = Room Aide' flag",
55 "Server timeout period",
56 "Initial access level",
57 "Registration requirements",
59 "Twit Detect target room",
60 "Maximum concurrent sessions",
62 "Restrict Internet mail flag",
64 "Name of bit bucket subdirectory",
65 "System net password",
70 char *setup_text[] = {
73 "Enter the full pathname of the directory in which the BBS you are",
74 "creating or updating resides. If you specify a directory other than the",
75 "default, you will need to specify the -h flag to the server when you start",
79 "This is the name your system is known by on a Citadel/UX network. It",
80 "should be 8 characters or less, and should generally be comprised only of",
81 "letters. You can also use numbers and hyphens if necessary.",
84 "This is the name your system is known by on the Internet.",
85 "If you're not on the Internet, simply set this to your",
86 "node name followed by '.UUCP'.",
89 "This is a longer description of your system, readable by",
90 "us mere humans. It can be up to 20 characters long and it",
91 "can have spaces in it. Note that if you are part of a",
92 "Cit86Net, this is the name your system will be known by on",
96 "This is the main dialup number for your system. If yours",
97 "can not be dialed into, then make one up! It should be in",
98 "the format 'US 000 000 0000' - the US is your country code",
99 "(look it up if you're not in the United States) and the",
100 "rest is, of course, your area code and phone number.",
101 "This doesn't have any use in Citadel/UX, but gateways to",
102 "other networks may require it, and someday we may use this",
103 "to have the networker automatically build a BBS list.",
106 "Enter the city and state your system is located in.",
109 "Enter the name of the system administrator (which is probably you).",
110 "When an account is created with this name, it will automatically be",
111 "assigned the highest access level.",
114 "You should create a user called 'bbs', 'guest', 'citadel', or something",
115 "similar, that will allow users a way into your BBS. The server will run",
116 "under this user ID. Please specify that (numeric) user ID here.",
119 "Citadel uses a (very) simple password encryption scheme",
120 "to thwart breakins that could occur if someone snatched",
121 "a copy of your userlog. This parameter is part of the",
122 "algorithm, so that the code can be different on each",
123 "system. Once it has been set, DO NOT change it --",
124 "otherwise no one will be able to log in!",
127 "This is a boolean value. If you set it to 1, anyone who",
128 "creates a class 3 (passworded) or class 4 (invitation",
129 "only) room will automatically become the Room Aide for",
130 "that room, allowing them to edit it, delete/move messages,",
131 "etc. This is an administrative decision: it works well on",
132 "some systems, and not so well on others. Set this to 0 to",
133 "disable this function.",
136 "This setting specifies how long a server session may sit idle before it is",
137 "automatically terminated. The recommended value is 900 seconds (15",
138 "minutes). Note that this has *nothing* to do with any watchdog timer that",
139 "is presented to the user. The server's timeout is intended to kill idle or",
140 "zombie sessions running on a network, etc. ",
141 "You MUST set this to a reasonable value. Setting it to zero will cause",
142 "the server to malfunction.",
145 "This is the access level new users are assigned.",
147 "The most common settings for this will be either 1, for",
148 "systems which require new user validation by the system",
149 "administrator ('sysop' is a word for people who run DOS",
150 "boards!), or 4, for systems which give instant access.",
151 "The current access levels available are:",
154 "'Registration' refers to the boring part of logging into a BBS for the first",
155 "time: typing your name, address, and telephone number. Set this value to 1",
156 "to automatically do registration for new users, or 0 to not auto-register.",
157 "Optionally, you could set it to, say, 2, to auto-register on a user's second",
158 "call, but there really isn't much point to doing this. The recommended",
159 "value is 1 if you've set your inital access level to 1, or 0 if you've set",
160 "your initial access level to something higher.",
163 "Every BBS has its share of problem users. This is one",
164 "good way to deal with them: if you enable this option,",
165 "anyone you flag as a 'problem user' (access level 2) can",
166 "post anywhere they want, but their messages will all be",
167 "automatically moved to a room of your choosing. Set this",
168 "value to 1 to enable Twit Detect, or 0 to disable it.",
171 "This is the name of the room that problem user messages",
172 "get moved to if you have Twit Detect enabled.",
173 "(Note: don't forget to *create* this room!)",
176 "This is the maximum number of concurrent Citadel sessions which may be",
177 "running at any given time. Use this to keep very busy systems from being",
179 " Set this value to 0 to allow an unlimited number of sessions.",
182 "This is the prompt that appears after each screenful of",
183 "text - for users that have chosen that option. Usually",
184 "a simple '<more>' will do, but some folks like to be",
188 "If you have a gateway set up to allow Citadel users to",
189 "send Internet mail, with sendmail, qmail, or whatever, and",
190 "you wish to restrict this to only users to whom you have",
191 "given this privilege, set this flag to 1. Otherwise, set",
192 "it to 0 to allow everyone to send Internet mail.",
193 "(Obviously, if your system doesn't have the ability to",
194 "send mail to the outside world, this is all irrelevant.)",
197 "This parameter is meaningless and should be removed.",
200 "Select the name of a subdirectory (relative to the main",
201 "Citadel directory - do not type an absolute pathname!) in",
202 "which to place arriving file transfers that otherwise",
203 "don't have a home.",
206 "If you use Citadel client/server sessions to transport network spool data",
207 "between systems, this is the password other systems will use to authenticate",
208 "themselves as network nodes rather than regular callers.",
211 "Specify the TCP port number on which your server will run. Normally, this",
212 "will be port 504, which is the official port assigned by the IANA for",
213 "Citadel servers. You'll only need to specify a different port number if",
214 "you run multiple BBS's on the same computer and there's something else",
215 "already using port 504.",
228 "Setup has detected that you currently have data files from a Citadel/UX",
229 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
230 "files to version 4.0x format.",
231 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
232 "files, and run setup again.",
241 struct config config;
244 void cleanup(int exitcode) {
246 if (setup_type == UI_CURSES) {
253 /* Do an 'init q' if we need to. When we hit the right one, init
254 * will take over and setup won't come back because we didn't do a
255 * fork(). If init isn't found, we fall through the bottom of the
256 * loop and setup exits quietly.
259 execlp("/sbin/init", "init", "q", NULL);
260 execlp("/usr/sbin/init", "init", "q", NULL);
261 execlp("/bin/init", "init", "q", NULL);
262 execlp("/usr/bin/init", "init", "q", NULL);
263 execlp("init", "init", "q", NULL);
271 void getlin(yp,xp,string,lim) /* Gets a line from the terminal */
272 int yp,xp; /* Where on the screen to start */
273 char string[]; /* Pointer to string buffer */
274 int lim; /* Maximum length - if negative, no-show */
279 if (lim<0) { lim=(0-lim); flag=1; }
282 for (a=0; a<lim; ++a) addch('-');
285 for (a=0; a<lim; ++a) addch(' ');
287 printw("%s", string);
288 GLA: move(yp,xp+strlen(string));
294 if ((a==8)&&(strlen(string)==0)) goto GLA;
295 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
296 if ((a==8)&&(string[0]!=0)) {
297 string[strlen(string)-1]=0;
298 move(yp,xp+strlen(string));
302 if ((a==13)||(a==10)) {
305 for (a=0; a<lim; ++a) addch(' ');
306 mvprintw(yp,xp,"%s",string);
313 if (flag==0) addch(a);
314 if (flag==1) addch('*');
323 if (setup_type == UI_TEXT) {
324 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
333 if (setup_type == UI_CURSES) {
334 mvprintw(20, 0, "Press any key to continue... ");
340 printf("Press return to continue...");
341 fgets(junk, 5, stdin);
353 printf("%s\nYes/No --> ",question);
354 fgets(buf, 4096, stdin);
355 answer=tolower(buf[0]);
356 if (answer=='y') answer=1;
357 else if (answer=='n') answer=0;
358 } while ((answer<0)||(answer>1));
362 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
363 answer = ( (system(buf)==0) ? 1 : 0);
370 mvprintw(1, 20, "Question");
372 mvprintw(10, 0, "%-80s", question);
373 mvprintw(20, 0, "%80s", "");
374 mvprintw(20, 0, "Yes/No -> ");
377 answer=tolower(answer);
378 if (answer=='y') answer=1;
379 else if (answer=='n') answer=0;
380 } while ((answer<0)||(answer>1));
390 void dump_access_levels() {
392 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
395 void get_setup_msg(char *dispbuf, int msgnum) {
400 while (atol(setup_text[a]) != msgnum) ++a;
404 strcat(dispbuf, setup_text[a++]);
405 strcat(dispbuf, "\n");
406 } while(atol(setup_text[a])!=(msgnum+1));
409 void print_setup(msgnum) {
412 get_setup_msg(dispbuf, msgnum);
413 printf("\n\n%s\n\n", dispbuf);
417 void important_message(char *title, char *msgtext) {
423 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");
424 printf(" %s \n\n%s\n\n", title, msgtext);
429 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
438 printw(" Important Message ");
441 printw("%s", msgtext);
450 void important_msgnum(int msgnum) {
453 get_setup_msg(dispbuf, msgnum);
454 important_message("Important Message", dispbuf);
457 void display_error(char *error_message) {
458 important_message("Error", error_message);
461 void progress(text,curr,cmax)
465 static long dots_printed;
468 static FILE *gauge = NULL;
476 printf("..........................");
477 printf("..........................");
478 printf("..........................\r");
482 else if (curr==cmax) {
483 printf("\r%79s\n","");
486 a=(curr * 100) / cmax;
488 while (dots_printed < a) {
503 printf("..........................");
504 printf("..........................");
505 printf("..........................\r");
509 else if (curr==cmax) {
514 a=(curr * 100) / cmax;
518 while (dots_printed < a) {
528 if ( (curr == 0) && (gauge == NULL) ) {
529 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
531 gauge = (FILE *) popen(gcmd, "w");
534 else if (curr==cmax) {
535 fprintf(gauge, "100\n");
540 a=(curr * 100) / cmax;
542 fprintf(gauge, "%ld\n", a);
554 * check_services_entry() -- Make sure "citadel" is in /etc/services
557 void check_services_entry() {
562 "There is no '%s' entry in /etc/services. Would you like to add one?",
565 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
566 if (yesno(question)==1) {
567 sfp = fopen("/etc/services", "a");
569 display_error(strerror(errno));
572 fprintf(sfp, "%s 504/tcp\n",
583 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
586 void check_inittab_entry() {
589 char looking_for[256];
595 /* Determine the fully qualified path name of citserver */
596 sprintf(looking_for, "%s/citserver ", BBSDIR);
598 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
599 * an entry is found which we believe starts citserver.
601 infp = fopen("/etc/inittab", "r");
603 display_error(strerror(errno));
606 while (fgets(buf, 256, infp) != NULL) {
607 buf[strlen(buf) - 1] = 0;
608 ptr = strtok(buf, ":");
609 ptr = strtok(NULL, ":");
610 ptr = strtok(NULL, ":");
611 ptr = strtok(NULL, ":");
613 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
621 /* If there's already an entry, then we have nothing left to do. */
622 if (have_entry > 0) return;
624 /* Otherwise, prompt the user to create an entry. */
626 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
628 if (yesno(question)==0) return;
630 /* Generate a unique entry name for /etc/inittab */
631 sprintf(entryname, "c0");
634 if (entryname[1] > '9') {
637 if (entryname[0] > 'z') {
639 "Can't generate a unique entry name");
644 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
645 } while(system(buf)==0);
647 /* Now write it out to /etc/inittab */
648 infp = fopen("/etc/inittab", "a");
650 display_error(strerror(errno));
653 fprintf(infp, "# Start the Citadel/UX server...\n");
654 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
655 entryname, looking_for, setup_directory);
663 void set_str_val(int msgpos, char str[]) {
669 sprintf(tempfile, "/tmp/setup.%d", getpid());
671 switch (setup_type) {
673 title(setup_titles[msgpos]);
675 if (msgpos==11) dump_access_levels();
676 printf("This is currently set to:\n%s\n",str);
677 printf("Enter new value or press return to leave unchanged:\n");
678 fgets(buf, 4096, stdin);
679 buf[strlen(buf)-1] = 0;
680 if (strlen(buf)!=0) strcpy(str,buf);
683 get_setup_msg(setupmsg, msgpos);
685 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
686 setup_titles[msgpos],
689 if (system(buf)==0) {
690 fp = fopen(tempfile, "rb");
691 fgets(str, 4095, fp);
694 if (str[strlen(str)-1]==10)
695 str[strlen(str)-1]=0;
701 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
703 printw("%s", setup_titles[msgpos]);
706 get_setup_msg(setupmsg, msgpos);
707 printw("%s", setupmsg);
709 getlin(20, 0, str, 80);
715 void set_int_val(msgpos, ip)
719 sprintf(buf,"%d",(int)*ip);
720 set_str_val(msgpos, buf);
725 void set_char_val(msgpos, ip)
729 sprintf(buf,"%d",(int)*ip);
730 set_str_val(msgpos, buf);
731 *ip = (char)atoi(buf);
735 void set_long_val(msgpos, ip)
739 sprintf(buf,"%ld",*ip);
740 set_str_val(msgpos, buf);
745 int yesno_s(question) {
751 sprintf(tempfile, "/tmp/setup.%d", getpid());
752 switch (setup_type) {
756 if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
762 if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
769 sprintf(buf, "dialog --title \"Confirm file overwrite\" --menu \"\nAre you SURE you want to reinitialize this file?\n\" 13 80 2 NO \"No, don't overwrite\" YES \"Yes, overwrite the existing file\" 2>%s", tempfile);
771 if (a != 0) return(0);
772 fp = fopen(tempfile, "rb");
773 fgets(buf, 4095, fp);
776 if (buf[strlen(buf)-1]==10)
777 buf[strlen(buf)-1]=0;
778 return( (!strcmp(buf, "YES")) ? 1 : 0 );
783 return(0); /* just in case */
787 void edit_value(curr)
794 set_str_val(curr, config.c_nodename);
798 set_str_val(curr, config.c_fqdn);
802 set_str_val(curr, config.c_humannode);
806 set_str_val(curr, config.c_phonenum);
810 set_str_val(curr, config.c_bbs_city);
814 set_str_val(curr, config.c_sysadm);
818 set_int_val(curr, &config.c_bbsuid);
822 set_int_val(curr, &config.c_pwcrypt);
826 set_char_val(curr, &config.c_creataide);
830 set_int_val(curr, &config.c_sleeping);
834 set_char_val(curr, &config.c_initax);
838 set_char_val(curr, &config.c_regiscall);
842 set_char_val(curr, &config.c_twitdetect);
846 set_str_val(curr, config.c_twitroom);
850 set_int_val(curr, &config.c_maxsessions);
854 set_str_val(curr, config.c_moreprompt);
858 set_char_val(curr, &config.c_restrict);
862 set_long_val(curr, &config.c_msgbase);
866 set_str_val(curr, config.c_bucket_dir);
867 config.c_bucket_dir[14] = 0;
868 for (a=0; a<strlen(config.c_bucket_dir); ++a)
869 if (!isalpha(config.c_bucket_dir[a]))
870 strcpy(&config.c_bucket_dir[a],
871 &config.c_bucket_dir[a+1]);
875 set_str_val(curr, config.c_net_password);
879 set_int_val(curr, &config.c_port_number);
887 * (re-)write the config data to disk
889 void write_config_to_disk() {
892 fp=fopen("citadel.config","wb");
894 display_error("setup: cannot open citadel.config");
897 fwrite((char *)&config,sizeof(struct config),1,fp);
905 * Figure out what type of user interface we're going to use
913 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
924 void main(int argc, char *argv[]) {
929 int old_setup_level = 0;
932 /* set an invalid setup type */
935 /* parse command line args */
936 for (a=0; a<argc; ++a) {
937 if (!strncmp(argv[a], "-u", 2)) {
938 strcpy(aaa, argv[a]);
939 strcpy(aaa, &aaa[2]);
940 setup_type = atoi(aaa);
942 if (!strcmp(argv[a], "-i")) {
948 /* If a setup type was not specified, try to determine automatically
949 * the best one to use out of all available types.
951 if (setup_type < 0) {
952 setup_type = discover_ui();
956 if (setup_type == UI_CURSES) {
963 if (info_only == 1) {
964 important_message("Citadel/UX Setup", CITADEL);
968 strcpy(setup_directory, BBSDIR);
969 set_str_val(0, setup_directory);
970 if (chdir(setup_directory) != 0) {
971 important_message("Citadel/UX Setup",
972 "The directory you specified does not exist.");
980 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
984 system("exec clear");
990 * What we're going to try to do here is append a whole bunch of
991 * nulls to the citadel.config file, so we can keep the old config
992 * values if they exist, but if the file is missing or from an
993 * earlier version with a shorter config structure, when setup tries
994 * to read the old config parameters, they'll all come up zero.
995 * The length of the config file will be set to what it's supposed
996 * to be when we rewrite it, because we replace the old file with a
997 * completely new copy. (Neat, eh?)
1000 fp=fopen("citadel.config","ab");
1002 display_error("setup: cannot append citadel.config");
1005 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
1008 /* now we re-open it, and read the old or blank configuration */
1009 fp=fopen("citadel.config","rb");
1011 display_error("setup: cannot open citadel.config");
1014 fread((char *)&config,sizeof(struct config),1,fp);
1018 /* set some sample/default values in place of blanks... */
1019 if (strlen(config.c_nodename)==0)
1020 strcpy(config.c_nodename,"mysystem");
1021 if (strlen(config.c_fqdn)==0)
1022 sprintf(config.c_fqdn,"%s.UUCP",config.c_nodename);
1023 if (strlen(config.c_humannode)==0)
1024 strcpy(config.c_humannode,"My System");
1025 if (strlen(config.c_phonenum)==0)
1026 strcpy(config.c_phonenum,"US 800 555 1212");
1027 if (config.c_initax == 0)
1028 config.c_initax = 1;
1029 if (strlen(config.c_moreprompt)==0)
1030 strcpy(config.c_moreprompt,"<more>");
1031 if (strlen(config.c_twitroom)==0)
1032 strcpy(config.c_twitroom,"Trashcan");
1033 if (strlen(config.c_bucket_dir)==0)
1034 strcpy(config.c_bucket_dir,"bitbucket");
1035 if (strlen(config.c_net_password)==0)
1036 strcpy(config.c_net_password,"netpassword");
1037 if (config.c_port_number == 0) {
1038 config.c_port_number = 504;
1040 if (config.c_ipgm_secret == 0) {
1042 config.c_ipgm_secret = rand();
1044 if (config.c_sleeping == 0) {
1045 config.c_sleeping = 900;
1048 /* Go through a series of dialogs prompting for config info */
1049 for (curr = 1; curr <= MAXSETUP; ++curr) {
1054 if (setuid(config.c_bbsuid) != 0) {
1055 important_message("Citadel/UX Setup",
1056 "Failed to change the user ID to your BBS user.");
1061 /***** begin version update section ***** */
1062 /* take care of any updating that is necessary */
1064 old_setup_level = config.c_setup_level;
1066 if (old_setup_level == 0) goto NEW_INST;
1068 if (old_setup_level < 323) {
1069 important_message("Citadel/UX Setup",
1070 "This Citadel/UX installation is too old to be upgraded.");
1074 write_config_to_disk();
1076 if ((config.c_setup_level / 10) == 32) {
1077 important_msgnum(31);
1081 if (config.c_setup_level < 400) {
1082 config.c_setup_level = 400;
1085 /* end of 3.23 -> 4.00 update section */
1087 /* end of 4.00 -> 4.02 update section */
1089 old_setup_level = config.c_setup_level;
1091 /* end of version update section */
1094 config.c_setup_level = REV_LEVEL;
1095 write_config_to_disk();
1097 system("mkdir info 2>/dev/null"); /* Create these */
1098 system("mkdir bio 2>/dev/null");
1099 system("mkdir userpics 2>/dev/null");
1100 system("mkdir messages 2>/dev/null");
1101 system("mkdir help 2>/dev/null");
1102 system("mkdir images 2>/dev/null");
1103 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1107 system("rm -fr ./chatpipes 2>/dev/null"); /* Don't need these */
1108 system("rm -fr ./expressmsgs 2>/dev/null");
1111 check_services_entry(); /* Check /etc/services */
1112 check_inittab_entry(); /* Check /etc/inittab */
1114 progress("Setting file permissions", 0, 3);
1115 chown(".", config.c_bbsuid, getgid());
1116 progress("Setting file permissions", 1, 3);
1117 chown("citadel.config", config.c_bbsuid, getgid());
1118 progress("Setting file permissions", 2, 3);
1119 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1122 progress("Setting file permissions", 3, 3);
1124 important_message("Setup finished",
1125 "Setup is finished. You may now start the Citadel server.");