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 "'Room Creator = Room Aide' flag",
54 "Server timeout period",
55 "Initial access level",
56 "Registration requirements",
58 "Twit Detect target room",
59 "Maximum concurrent sessions",
61 "Restrict Internet mail flag",
62 "Name of bit bucket subdirectory",
63 "System net password",
68 char *setup_text[] = {
71 "Enter the full pathname of the directory in which the BBS you are",
72 "creating or updating resides. If you specify a directory other than the",
73 "default, you will need to specify the -h flag to the server when you start",
77 "This is the name your system is known by on a Citadel/UX network. It",
78 "should be 8 characters or less, and should generally be comprised only of",
79 "letters. You can also use numbers and hyphens if necessary.",
82 "This is the name your system is known by on the Internet.",
83 "If you're not on the Internet, simply set this to your",
84 "node name followed by '.UUCP'.",
87 "This is a longer description of your system, readable by",
88 "us mere humans. It can be up to 20 characters long and it",
89 "can have spaces in it. Note that if you are part of a",
90 "Cit86Net, this is the name your system will be known by on",
94 "This is the main dialup number for your system. If yours",
95 "can not be dialed into, then make one up! It should be in",
96 "the format 'US 000 000 0000' - the US is your country code",
97 "(look it up if you're not in the United States) and the",
98 "rest is, of course, your area code and phone number.",
99 "This doesn't have any use in Citadel/UX, but gateways to",
100 "other networks may require it, and someday we may use this",
101 "to have the networker automatically build a BBS list.",
104 "Enter the geographical location of your system (city and",
105 "state/province/country etc.)",
108 "Enter the name of the system administrator (which is probably you).",
109 "When an account is created with this name, it will automatically be",
110 "assigned the highest access level.",
113 "You should create a user called 'bbs', 'guest', 'citadel', or something",
114 "similar, that will allow users a way into your BBS. The server will run",
115 "under this user ID. Please specify that (numeric) user ID here.",
118 "This is a boolean value. If you set it to 1, anyone who",
119 "creates a class 3 (passworded) or class 4 (invitation",
120 "only) room will automatically become the Room Aide for",
121 "that room, allowing them to edit it, delete/move messages,",
122 "etc. This is an administrative decision: it works well on",
123 "some systems, and not so well on others. Set this to 0 to",
124 "disable this function.",
127 "This setting specifies how long a server session may sit idle before it is",
128 "automatically terminated. The recommended value is 900 seconds (15",
129 "minutes). Note that this has *nothing* to do with any watchdog timer that",
130 "is presented to the user. The server's timeout is intended to kill idle or",
131 "zombie sessions running on a network, etc. ",
132 "You MUST set this to a reasonable value. Setting it to zero will cause",
133 "the server to malfunction.",
136 "This is the access level new users are assigned.",
138 "The most common settings for this will be either 1, for",
139 "systems which require new user validation by the system",
140 "administrator ('sysop' is a word for people who run DOS",
141 "boards!), or 4, for systems which give instant access.",
142 "The current access levels available are:",
145 "'Registration' refers to the boring part of logging into a BBS for the first",
146 "time: typing your name, address, and telephone number. Set this value to 1",
147 "to automatically do registration for new users, or 0 to not auto-register.",
148 "Optionally, you could set it to, say, 2, to auto-register on a user's second",
149 "call, but there really isn't much point to doing this. The recommended",
150 "value is 1 if you've set your inital access level to 1, or 0 if you've set",
151 "your initial access level to something higher.",
154 "Every BBS has its share of problem users. This is one",
155 "good way to deal with them: if you enable this option,",
156 "anyone you flag as a 'problem user' (access level 2) can",
157 "post anywhere they want, but their messages will all be",
158 "automatically moved to a room of your choosing. Set this",
159 "value to 1 to enable Twit Detect, or 0 to disable it.",
162 "This is the name of the room that problem user messages",
163 "get moved to if you have Twit Detect enabled.",
164 "(Note: don't forget to *create* this room!)",
167 "This is the maximum number of concurrent Citadel sessions which may be",
168 "running at any given time. Use this to keep very busy systems from being",
170 " Set this value to 0 to allow an unlimited number of sessions.",
173 "This is the prompt that appears after each screenful of",
174 "text - for users that have chosen that option. Usually",
175 "a simple '<more>' will do, but some folks like to be",
179 "If you have a gateway set up to allow Citadel users to",
180 "send Internet mail, with sendmail, qmail, or whatever, and",
181 "you wish to restrict this to only users to whom you have",
182 "given this privilege, set this flag to 1. Otherwise, set",
183 "it to 0 to allow everyone to send Internet mail.",
184 "(Obviously, if your system doesn't have the ability to",
185 "send mail to the outside world, this is all irrelevant.)",
188 "Select the name of a subdirectory (relative to the main",
189 "Citadel directory - do not type an absolute pathname!) in",
190 "which to place arriving file transfers that otherwise",
191 "don't have a home.",
194 "If you use Citadel client/server sessions to transport network spool data",
195 "between systems, this is the password other systems will use to authenticate",
196 "themselves as network nodes rather than regular callers.",
199 "Specify the TCP port number on which your server will run. Normally, this",
200 "will be port 504, which is the official port assigned by the IANA for",
201 "Citadel servers. You'll only need to specify a different port number if",
202 "you run multiple BBS's on the same computer and there's something else",
203 "already using port 504.",
218 "Setup has detected that you currently have data files from a Citadel/UX",
219 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
220 "files to version 4.0x format.",
221 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
222 "files, and run setup again.",
231 struct config config;
234 void cleanup(int exitcode) {
236 if (setup_type == UI_CURSES) {
243 /* Do an 'init q' if we need to. When we hit the right one, init
244 * will take over and setup won't come back because we didn't do a
245 * fork(). If init isn't found, we fall through the bottom of the
246 * loop and setup exits quietly.
249 execlp("/sbin/init", "init", "q", NULL);
250 execlp("/usr/sbin/init", "init", "q", NULL);
251 execlp("/bin/init", "init", "q", NULL);
252 execlp("/usr/bin/init", "init", "q", NULL);
253 execlp("init", "init", "q", NULL);
261 void getlin(yp,xp,string,lim) /* Gets a line from the terminal */
262 int yp,xp; /* Where on the screen to start */
263 char string[]; /* Pointer to string buffer */
264 int lim; /* Maximum length - if negative, no-show */
269 if (lim<0) { lim=(0-lim); flag=1; }
272 for (a=0; a<lim; ++a) addch('-');
275 for (a=0; a<lim; ++a) addch(' ');
277 printw("%s", string);
278 GLA: move(yp,xp+strlen(string));
284 if ((a==8)&&(strlen(string)==0)) goto GLA;
285 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
286 if ((a==8)&&(string[0]!=0)) {
287 string[strlen(string)-1]=0;
288 move(yp,xp+strlen(string));
292 if ((a==13)||(a==10)) {
295 for (a=0; a<lim; ++a) addch(' ');
296 mvprintw(yp,xp,"%s",string);
303 if (flag==0) addch(a);
304 if (flag==1) addch('*');
313 if (setup_type == UI_TEXT) {
314 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
323 if (setup_type == UI_CURSES) {
324 mvprintw(20, 0, "Press any key to continue... ");
330 printf("Press return to continue...");
331 fgets(junk, 5, stdin);
343 printf("%s\nYes/No --> ",question);
344 fgets(buf, 4096, stdin);
345 answer=tolower(buf[0]);
346 if (answer=='y') answer=1;
347 else if (answer=='n') answer=0;
348 } while ((answer<0)||(answer>1));
352 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
353 answer = ( (system(buf)==0) ? 1 : 0);
360 mvprintw(1, 20, "Question");
362 mvprintw(10, 0, "%-80s", question);
363 mvprintw(20, 0, "%80s", "");
364 mvprintw(20, 0, "Yes/No -> ");
367 answer=tolower(answer);
368 if (answer=='y') answer=1;
369 else if (answer=='n') answer=0;
370 } while ((answer<0)||(answer>1));
380 void dump_access_levels() {
382 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
385 void get_setup_msg(char *dispbuf, int msgnum) {
390 while (atol(setup_text[a]) != msgnum) ++a;
394 strcat(dispbuf, setup_text[a++]);
395 strcat(dispbuf, "\n");
396 } while(atol(setup_text[a])!=(msgnum+1));
399 void print_setup(msgnum) {
402 get_setup_msg(dispbuf, msgnum);
403 printf("\n\n%s\n\n", dispbuf);
407 void important_message(char *title, char *msgtext) {
413 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");
414 printf(" %s \n\n%s\n\n", title, msgtext);
419 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
428 printw(" Important Message ");
431 printw("%s", msgtext);
440 void important_msgnum(int msgnum) {
443 get_setup_msg(dispbuf, msgnum);
444 important_message("Important Message", dispbuf);
447 void display_error(char *error_message) {
448 important_message("Error", error_message);
451 void progress(text,curr,cmax)
455 static long dots_printed;
458 static FILE *gauge = NULL;
466 printf("..........................");
467 printf("..........................");
468 printf("..........................\r");
472 else if (curr==cmax) {
473 printf("\r%79s\n","");
476 a=(curr * 100) / cmax;
478 while (dots_printed < a) {
493 printf("..........................");
494 printf("..........................");
495 printf("..........................\r");
499 else if (curr==cmax) {
504 a=(curr * 100) / cmax;
508 while (dots_printed < a) {
518 if ( (curr == 0) && (gauge == NULL) ) {
519 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
521 gauge = (FILE *) popen(gcmd, "w");
524 else if (curr==cmax) {
525 fprintf(gauge, "100\n");
530 a=(curr * 100) / cmax;
532 fprintf(gauge, "%ld\n", a);
544 * check_services_entry() -- Make sure "citadel" is in /etc/services
547 void check_services_entry() {
552 "There is no '%s' entry in /etc/services. Would you like to add one?",
555 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
556 if (yesno(question)==1) {
557 sfp = fopen("/etc/services", "a");
559 display_error(strerror(errno));
562 fprintf(sfp, "%s 504/tcp\n",
573 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
576 void check_inittab_entry() {
579 char looking_for[256];
585 /* Determine the fully qualified path name of citserver */
586 sprintf(looking_for, "%s/citserver ", BBSDIR);
588 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
589 * an entry is found which we believe starts citserver.
591 infp = fopen("/etc/inittab", "r");
593 display_error(strerror(errno));
596 while (fgets(buf, 256, infp) != NULL) {
597 buf[strlen(buf) - 1] = 0;
598 ptr = strtok(buf, ":");
599 ptr = strtok(NULL, ":");
600 ptr = strtok(NULL, ":");
601 ptr = strtok(NULL, ":");
603 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
611 /* If there's already an entry, then we have nothing left to do. */
612 if (have_entry > 0) return;
614 /* Otherwise, prompt the user to create an entry. */
616 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
618 if (yesno(question)==0) return;
620 /* Generate a unique entry name for /etc/inittab */
621 sprintf(entryname, "c0");
624 if (entryname[1] > '9') {
627 if (entryname[0] > 'z') {
629 "Can't generate a unique entry name");
634 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
635 } while(system(buf)==0);
637 /* Now write it out to /etc/inittab */
638 infp = fopen("/etc/inittab", "a");
640 display_error(strerror(errno));
643 fprintf(infp, "# Start the Citadel/UX server...\n");
644 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
645 entryname, looking_for, setup_directory);
653 void set_str_val(int msgpos, char str[]) {
659 sprintf(tempfile, "/tmp/setup.%d", getpid());
661 switch (setup_type) {
663 title(setup_titles[msgpos]);
665 if (msgpos==11) dump_access_levels();
666 printf("This is currently set to:\n%s\n",str);
667 printf("Enter new value or press return to leave unchanged:\n");
668 fgets(buf, 4096, stdin);
669 buf[strlen(buf)-1] = 0;
670 if (strlen(buf)!=0) strcpy(str,buf);
673 get_setup_msg(setupmsg, msgpos);
675 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
676 setup_titles[msgpos],
679 if (system(buf)==0) {
680 fp = fopen(tempfile, "rb");
681 fgets(str, 4095, fp);
684 if (str[strlen(str)-1]==10)
685 str[strlen(str)-1]=0;
691 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
693 printw("%s", setup_titles[msgpos]);
696 get_setup_msg(setupmsg, msgpos);
697 printw("%s", setupmsg);
699 getlin(20, 0, str, 80);
705 void set_int_val(msgpos, ip)
709 sprintf(buf,"%d",(int)*ip);
710 set_str_val(msgpos, buf);
715 void set_char_val(msgpos, ip)
719 sprintf(buf,"%d",(int)*ip);
720 set_str_val(msgpos, buf);
721 *ip = (char)atoi(buf);
725 void set_long_val(msgpos, ip)
729 sprintf(buf,"%ld",*ip);
730 set_str_val(msgpos, buf);
735 int yesno_s(question) {
741 sprintf(tempfile, "/tmp/setup.%d", getpid());
742 switch (setup_type) {
746 if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
752 if (a==1) a=yesno("Are you SURE you want to reinitialize this file? ");
759 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);
761 if (a != 0) return(0);
762 fp = fopen(tempfile, "rb");
763 fgets(buf, 4095, fp);
766 if (buf[strlen(buf)-1]==10)
767 buf[strlen(buf)-1]=0;
768 return( (!strcmp(buf, "YES")) ? 1 : 0 );
773 return(0); /* just in case */
777 void edit_value(curr)
784 set_str_val(curr, config.c_nodename);
788 set_str_val(curr, config.c_fqdn);
792 set_str_val(curr, config.c_humannode);
796 set_str_val(curr, config.c_phonenum);
800 set_str_val(curr, config.c_bbs_city);
804 set_str_val(curr, config.c_sysadm);
808 set_int_val(curr, &config.c_bbsuid);
812 set_char_val(curr, &config.c_creataide);
816 set_int_val(curr, &config.c_sleeping);
820 set_char_val(curr, &config.c_initax);
824 set_char_val(curr, &config.c_regiscall);
828 set_char_val(curr, &config.c_twitdetect);
832 set_str_val(curr, config.c_twitroom);
836 set_int_val(curr, &config.c_maxsessions);
840 set_str_val(curr, config.c_moreprompt);
844 set_char_val(curr, &config.c_restrict);
848 set_str_val(curr, config.c_bucket_dir);
849 config.c_bucket_dir[14] = 0;
850 for (a=0; a<strlen(config.c_bucket_dir); ++a)
851 if (!isalpha(config.c_bucket_dir[a]))
852 strcpy(&config.c_bucket_dir[a],
853 &config.c_bucket_dir[a+1]);
857 set_str_val(curr, config.c_net_password);
861 set_int_val(curr, &config.c_port_number);
869 * (re-)write the config data to disk
871 void write_config_to_disk() {
874 fp=fopen("citadel.config","wb");
876 display_error("setup: cannot open citadel.config");
879 fwrite((char *)&config,sizeof(struct config),1,fp);
887 * Figure out what type of user interface we're going to use
895 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
906 void main(int argc, char *argv[]) {
911 int old_setup_level = 0;
914 /* set an invalid setup type */
917 /* parse command line args */
918 for (a=0; a<argc; ++a) {
919 if (!strncmp(argv[a], "-u", 2)) {
920 strcpy(aaa, argv[a]);
921 strcpy(aaa, &aaa[2]);
922 setup_type = atoi(aaa);
924 if (!strcmp(argv[a], "-i")) {
930 /* If a setup type was not specified, try to determine automatically
931 * the best one to use out of all available types.
933 if (setup_type < 0) {
934 setup_type = discover_ui();
938 if (setup_type == UI_CURSES) {
945 if (info_only == 1) {
946 important_message("Citadel/UX Setup", CITADEL);
950 strcpy(setup_directory, BBSDIR);
951 set_str_val(0, setup_directory);
952 if (chdir(setup_directory) != 0) {
953 important_message("Citadel/UX Setup",
954 "The directory you specified does not exist.");
962 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
966 system("exec clear");
972 * What we're going to try to do here is append a whole bunch of
973 * nulls to the citadel.config file, so we can keep the old config
974 * values if they exist, but if the file is missing or from an
975 * earlier version with a shorter config structure, when setup tries
976 * to read the old config parameters, they'll all come up zero.
977 * The length of the config file will be set to what it's supposed
978 * to be when we rewrite it, because we replace the old file with a
979 * completely new copy. (Neat, eh?)
982 fp=fopen("citadel.config","ab");
984 display_error("setup: cannot append citadel.config");
987 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
990 /* now we re-open it, and read the old or blank configuration */
991 fp=fopen("citadel.config","rb");
993 display_error("setup: cannot open citadel.config");
996 fread((char *)&config,sizeof(struct config),1,fp);
1000 /* set some sample/default values in place of blanks... */
1001 if (strlen(config.c_nodename)==0)
1002 strcpy(config.c_nodename,"mysystem");
1003 if (strlen(config.c_fqdn)==0)
1004 sprintf(config.c_fqdn,"%s.UUCP",config.c_nodename);
1005 if (strlen(config.c_humannode)==0)
1006 strcpy(config.c_humannode,"My System");
1007 if (strlen(config.c_phonenum)==0)
1008 strcpy(config.c_phonenum,"US 800 555 1212");
1009 if (config.c_initax == 0)
1010 config.c_initax = 1;
1011 if (strlen(config.c_moreprompt)==0)
1012 strcpy(config.c_moreprompt,"<more>");
1013 if (strlen(config.c_twitroom)==0)
1014 strcpy(config.c_twitroom,"Trashcan");
1015 if (strlen(config.c_bucket_dir)==0)
1016 strcpy(config.c_bucket_dir,"bitbucket");
1017 if (strlen(config.c_net_password)==0)
1018 strcpy(config.c_net_password,"netpassword");
1019 if (config.c_port_number == 0) {
1020 config.c_port_number = 504;
1022 if (config.c_ipgm_secret == 0) {
1024 config.c_ipgm_secret = rand();
1026 if (config.c_sleeping == 0) {
1027 config.c_sleeping = 900;
1030 /* Go through a series of dialogs prompting for config info */
1031 for (curr = 1; curr <= MAXSETUP; ++curr) {
1036 if (setuid(config.c_bbsuid) != 0) {
1037 important_message("Citadel/UX Setup",
1038 "Failed to change the user ID to your BBS user.");
1043 /***** begin version update section ***** */
1044 /* take care of any updating that is necessary */
1046 old_setup_level = config.c_setup_level;
1048 if (old_setup_level == 0) goto NEW_INST;
1050 if (old_setup_level < 323) {
1051 important_message("Citadel/UX Setup",
1052 "This Citadel/UX installation is too old to be upgraded.");
1056 write_config_to_disk();
1058 if ((config.c_setup_level / 10) == 32) {
1059 important_msgnum(31);
1063 if (config.c_setup_level < 400) {
1064 config.c_setup_level = 400;
1067 /* end of 3.23 -> 4.00 update section */
1069 /* end of 4.00 -> 4.02 update section */
1071 old_setup_level = config.c_setup_level;
1073 /* end of version update section */
1076 config.c_setup_level = REV_LEVEL;
1077 write_config_to_disk();
1079 system("mkdir info 2>/dev/null"); /* Create these */
1080 system("mkdir bio 2>/dev/null");
1081 system("mkdir userpics 2>/dev/null");
1082 system("mkdir messages 2>/dev/null");
1083 system("mkdir help 2>/dev/null");
1084 system("mkdir images 2>/dev/null");
1085 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1089 system("rm -fr ./chatpipes 2>/dev/null"); /* Don't need these */
1090 system("rm -fr ./expressmsgs 2>/dev/null");
1093 check_services_entry(); /* Check /etc/services */
1094 check_inittab_entry(); /* Check /etc/inittab */
1096 progress("Setting file permissions", 0, 3);
1097 chown(".", config.c_bbsuid, getgid());
1098 progress("Setting file permissions", 1, 3);
1099 chown("citadel.config", config.c_bbsuid, getgid());
1100 progress("Setting file permissions", 2, 3);
1101 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1104 progress("Setting file permissions", 3, 3);
1106 important_message("Setup finished",
1107 "Setup is finished. You may now start the Citadel server.");