2 * Citadel/UX setup program
4 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
13 #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, or 4, for systems which give instant access.",
141 "The current access levels available are:",
144 "'Registration' refers to the boring part of logging into a BBS for the first",
145 "time: typing your name, address, and telephone number. Set this value to 1",
146 "to automatically do registration for new users, or 0 to not auto-register.",
147 "Optionally, you could set it to, say, 2, to auto-register on a user's second",
148 "call, but there really isn't much point to doing this. The recommended",
149 "value is 1 if you've set your inital access level to 1, or 0 if you've set",
150 "your initial access level to something higher.",
153 "Every BBS has its share of problem users. This is one",
154 "good way to deal with them: if you enable this option,",
155 "anyone you flag as a 'problem user' (access level 2) can",
156 "post anywhere they want, but their messages will all be",
157 "automatically moved to a room of your choosing. Set this",
158 "value to 1 to enable Twit Detect, or 0 to disable it.",
161 "This is the name of the room that problem user messages",
162 "get moved to if you have Twit Detect enabled.",
163 "(Note: don't forget to *create* this room!)",
166 "This is the maximum number of concurrent Citadel sessions which may be",
167 "running at any given time. Use this to keep very busy systems from being",
169 " Set this value to 0 to allow an unlimited number of sessions.",
172 "This is the prompt that appears after each screenful of",
173 "text - for users that have chosen that option. Usually",
174 "a simple '<more>' will do, but some folks like to be",
178 "If you have a gateway set up to allow Citadel users to",
179 "send Internet mail, with sendmail, qmail, or whatever, and",
180 "you wish to restrict this to only users to whom you have",
181 "given this privilege, set this flag to 1. Otherwise, set",
182 "it to 0 to allow everyone to send Internet mail.",
183 "(Obviously, if your system doesn't have the ability to",
184 "send mail to the outside world, this is all irrelevant.)",
187 "Select the name of a subdirectory (relative to the main",
188 "Citadel directory - do not type an absolute pathname!) in",
189 "which to place arriving file transfers that otherwise",
190 "don't have a home.",
193 "If you use Citadel client/server sessions to transport network spool data",
194 "between systems, this is the password other systems will use to authenticate",
195 "themselves as network nodes rather than regular callers.",
198 "Specify the TCP port number on which your server will run. Normally, this",
199 "will be port 504, which is the official port assigned by the IANA for",
200 "Citadel servers. You'll only need to specify a different port number if",
201 "you run multiple BBS's on the same computer and there's something else",
202 "already using port 504.",
217 "Setup has detected that you currently have data files from a Citadel/UX",
218 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
219 "files to version 4.0x format.",
220 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
221 "files, and run setup again.",
227 struct config config;
230 void cleanup(int exitcode) {
232 if (setup_type == UI_CURSES) {
239 /* Do an 'init q' if we need to. When we hit the right one, init
240 * will take over and setup won't come back because we didn't do a
241 * fork(). If init isn't found, we fall through the bottom of the
242 * loop and setup exits quietly.
245 execlp("/sbin/init", "init", "q", NULL);
246 execlp("/usr/sbin/init", "init", "q", NULL);
247 execlp("/bin/init", "init", "q", NULL);
248 execlp("/usr/bin/init", "init", "q", NULL);
249 execlp("init", "init", "q", NULL);
257 void getlin(int yp, int xp, char *string, int lim) /* Gets a line from the terminal */
258 /* Where on the screen to start */
259 /* Pointer to string buffer */
260 /* Maximum length - if negative, no-show */
265 if (lim<0) { lim=(0-lim); flag=1; }
268 for (a=0; a<lim; ++a) addch('-');
271 for (a=0; a<lim; ++a) addch(' ');
273 printw("%s", string);
274 GLA: move(yp,xp+strlen(string));
280 if ((a==8)&&(strlen(string)==0)) goto GLA;
281 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
282 if ((a==8)&&(string[0]!=0)) {
283 string[strlen(string)-1]=0;
284 move(yp,xp+strlen(string));
288 if ((a==13)||(a==10)) {
291 for (a=0; a<lim; ++a) addch(' ');
292 mvprintw(yp,xp,"%s",string);
299 if (flag==0) addch(a);
300 if (flag==1) addch('*');
307 void title(char *text)
309 if (setup_type == UI_TEXT) {
310 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
315 void hit_any_key(void) {
319 if (setup_type == UI_CURSES) {
320 mvprintw(20, 0, "Press any key to continue... ");
326 printf("Press return to continue...");
327 fgets(junk, 5, stdin);
330 int yesno(char *question)
339 printf("%s\nYes/No --> ",question);
340 fgets(buf, 4096, stdin);
341 answer=tolower(buf[0]);
342 if (answer=='y') answer=1;
343 else if (answer=='n') answer=0;
344 } while ((answer<0)||(answer>1));
348 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
349 answer = ( (system(buf)==0) ? 1 : 0);
356 mvprintw(1, 20, "Question");
358 mvprintw(10, 0, "%-80s", question);
359 mvprintw(20, 0, "%80s", "");
360 mvprintw(20, 0, "Yes/No -> ");
363 answer=tolower(answer);
364 if (answer=='y') answer=1;
365 else if (answer=='n') answer=0;
366 } while ((answer<0)||(answer>1));
376 void dump_access_levels(void) {
378 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
381 void get_setup_msg(char *dispbuf, int msgnum) {
386 while (atol(setup_text[a]) != msgnum) ++a;
390 strcat(dispbuf, setup_text[a++]);
391 strcat(dispbuf, "\n");
392 } while(atol(setup_text[a])!=(msgnum+1));
395 void print_setup(int msgnum) {
398 get_setup_msg(dispbuf, msgnum);
399 printf("\n\n%s\n\n", dispbuf);
403 void important_message(char *title, char *msgtext) {
409 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");
410 printf(" %s \n\n%s\n\n", title, msgtext);
415 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
424 printw(" Important Message ");
427 printw("%s", msgtext);
436 void important_msgnum(int msgnum) {
439 get_setup_msg(dispbuf, msgnum);
440 important_message("Important Message", dispbuf);
443 void display_error(char *error_message) {
444 important_message("Error", error_message);
447 void progress(char *text, long int curr, long int cmax)
449 static long dots_printed;
452 static FILE *gauge = NULL;
460 printf("..........................");
461 printf("..........................");
462 printf("..........................\r");
466 else if (curr==cmax) {
467 printf("\r%79s\n","");
470 a=(curr * 100) / cmax;
472 while (dots_printed < a) {
487 printf("..........................");
488 printf("..........................");
489 printf("..........................\r");
493 else if (curr==cmax) {
498 a=(curr * 100) / cmax;
502 while (dots_printed < a) {
512 if ( (curr == 0) && (gauge == NULL) ) {
513 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
515 gauge = (FILE *) popen(gcmd, "w");
518 else if (curr==cmax) {
519 fprintf(gauge, "100\n");
524 a=(curr * 100) / cmax;
526 fprintf(gauge, "%ld\n", a);
538 * check_services_entry() -- Make sure "citadel" is in /etc/services
541 void check_services_entry(void) {
546 "There is no '%s' entry in /etc/services. Would you like to add one?",
549 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
550 if (yesno(question)==1) {
551 sfp = fopen("/etc/services", "a");
553 display_error(strerror(errno));
556 fprintf(sfp, "%s 504/tcp\n",
567 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
570 void check_inittab_entry(void) {
573 char looking_for[256];
579 /* Determine the fully qualified path name of citserver */
580 sprintf(looking_for, "%s/citserver ", BBSDIR);
582 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
583 * an entry is found which we believe starts citserver.
585 infp = fopen("/etc/inittab", "r");
587 display_error(strerror(errno));
590 while (fgets(buf, 256, infp) != NULL) {
591 buf[strlen(buf) - 1] = 0;
592 ptr = strtok(buf, ":");
593 ptr = strtok(NULL, ":");
594 ptr = strtok(NULL, ":");
595 ptr = strtok(NULL, ":");
597 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
605 /* If there's already an entry, then we have nothing left to do. */
606 if (have_entry > 0) return;
608 /* Otherwise, prompt the user to create an entry. */
610 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
612 if (yesno(question)==0) return;
614 /* Generate a unique entry name for /etc/inittab */
615 sprintf(entryname, "c0");
618 if (entryname[1] > '9') {
621 if (entryname[0] > 'z') {
623 "Can't generate a unique entry name");
628 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
629 } while(system(buf)==0);
631 /* Now write it out to /etc/inittab */
632 infp = fopen("/etc/inittab", "a");
634 display_error(strerror(errno));
637 fprintf(infp, "# Start the Citadel/UX server...\n");
638 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
639 entryname, looking_for, setup_directory);
647 void set_str_val(int msgpos, char str[]) {
653 sprintf(tempfile, "/tmp/setup.%d", getpid());
655 switch (setup_type) {
657 title(setup_titles[msgpos]);
659 if (msgpos==11) dump_access_levels();
660 printf("This is currently set to:\n%s\n",str);
661 printf("Enter new value or press return to leave unchanged:\n");
662 fgets(buf, 4096, stdin);
663 buf[strlen(buf)-1] = 0;
664 if (strlen(buf)!=0) strcpy(str,buf);
667 get_setup_msg(setupmsg, msgpos);
669 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
670 setup_titles[msgpos],
673 if (system(buf)==0) {
674 fp = fopen(tempfile, "rb");
675 fgets(str, 4095, fp);
678 if (str[strlen(str)-1]==10)
679 str[strlen(str)-1]=0;
685 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
687 printw("%s", setup_titles[msgpos]);
690 get_setup_msg(setupmsg, msgpos);
691 printw("%s", setupmsg);
693 getlin(20, 0, str, 80);
699 void set_int_val(int msgpos, int *ip)
702 sprintf(buf,"%d",(int)*ip);
703 set_str_val(msgpos, buf);
708 void set_char_val(int msgpos, char *ip)
711 sprintf(buf,"%d",(int)*ip);
712 set_str_val(msgpos, buf);
713 *ip = (char)atoi(buf);
717 void set_long_val(int msgpos, long int *ip)
720 sprintf(buf,"%ld",*ip);
721 set_str_val(msgpos, buf);
726 void edit_value(int curr)
733 set_str_val(curr, config.c_nodename);
737 set_str_val(curr, config.c_fqdn);
741 set_str_val(curr, config.c_humannode);
745 set_str_val(curr, config.c_phonenum);
749 set_str_val(curr, config.c_bbs_city);
753 set_str_val(curr, config.c_sysadm);
757 set_int_val(curr, &config.c_bbsuid);
761 set_char_val(curr, &config.c_creataide);
765 set_int_val(curr, &config.c_sleeping);
769 set_char_val(curr, &config.c_initax);
773 set_char_val(curr, &config.c_regiscall);
777 set_char_val(curr, &config.c_twitdetect);
781 set_str_val(curr, config.c_twitroom);
785 set_int_val(curr, &config.c_maxsessions);
789 set_str_val(curr, config.c_moreprompt);
793 set_char_val(curr, &config.c_restrict);
797 set_str_val(curr, config.c_bucket_dir);
798 config.c_bucket_dir[14] = 0;
799 for (a=0; a<strlen(config.c_bucket_dir); ++a)
800 if (!isalpha(config.c_bucket_dir[a]))
801 strcpy(&config.c_bucket_dir[a],
802 &config.c_bucket_dir[a+1]);
806 set_str_val(curr, config.c_net_password);
810 set_int_val(curr, &config.c_port_number);
818 * (re-)write the config data to disk
820 void write_config_to_disk(void) {
823 fp=fopen("citadel.config","wb");
825 display_error("setup: cannot open citadel.config");
828 fwrite((char *)&config,sizeof(struct config),1,fp);
836 * Figure out what type of user interface we're going to use
838 int discover_ui(void) {
844 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
855 int main(int argc, char *argv[]) {
860 int old_setup_level = 0;
863 /* set an invalid setup type */
866 /* parse command line args */
867 for (a=0; a<argc; ++a) {
868 if (!strncmp(argv[a], "-u", 2)) {
869 strcpy(aaa, argv[a]);
870 strcpy(aaa, &aaa[2]);
871 setup_type = atoi(aaa);
873 if (!strcmp(argv[a], "-i")) {
879 /* If a setup type was not specified, try to determine automatically
880 * the best one to use out of all available types.
882 if (setup_type < 0) {
883 setup_type = discover_ui();
887 if (setup_type == UI_CURSES) {
894 if (info_only == 1) {
895 important_message("Citadel/UX Setup", CITADEL);
899 strcpy(setup_directory, BBSDIR);
900 set_str_val(0, setup_directory);
901 if (chdir(setup_directory) != 0) {
902 important_message("Citadel/UX Setup",
903 "The directory you specified does not exist.");
911 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
915 system("exec clear");
921 * What we're going to try to do here is append a whole bunch of
922 * nulls to the citadel.config file, so we can keep the old config
923 * values if they exist, but if the file is missing or from an
924 * earlier version with a shorter config structure, when setup tries
925 * to read the old config parameters, they'll all come up zero.
926 * The length of the config file will be set to what it's supposed
927 * to be when we rewrite it, because we replace the old file with a
928 * completely new copy. (Neat, eh?)
931 fp=fopen("citadel.config","ab");
933 display_error("setup: cannot append citadel.config");
936 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
939 /* now we re-open it, and read the old or blank configuration */
940 fp=fopen("citadel.config","rb");
942 display_error("setup: cannot open citadel.config");
945 fread((char *)&config,sizeof(struct config),1,fp);
949 /* set some sample/default values in place of blanks... */
950 if (strlen(config.c_nodename)==0)
951 strcpy(config.c_nodename,"mysystem");
952 if (strlen(config.c_fqdn)==0)
953 sprintf(config.c_fqdn,"%s.UUCP",config.c_nodename);
954 if (strlen(config.c_humannode)==0)
955 strcpy(config.c_humannode,"My System");
956 if (strlen(config.c_phonenum)==0)
957 strcpy(config.c_phonenum,"US 800 555 1212");
958 if (config.c_initax == 0)
960 if (strlen(config.c_moreprompt)==0)
961 strcpy(config.c_moreprompt,"<more>");
962 if (strlen(config.c_twitroom)==0)
963 strcpy(config.c_twitroom,"Trashcan");
964 if (strlen(config.c_bucket_dir)==0)
965 strcpy(config.c_bucket_dir,"bitbucket");
966 if (strlen(config.c_net_password)==0)
967 strcpy(config.c_net_password,"netpassword");
968 if (config.c_port_number == 0) {
969 config.c_port_number = 504;
971 if (config.c_ipgm_secret == 0) {
973 config.c_ipgm_secret = rand();
975 if (config.c_sleeping == 0) {
976 config.c_sleeping = 900;
979 /* Go through a series of dialogs prompting for config info */
980 for (curr = 1; curr <= MAXSETUP; ++curr) {
985 if (setuid(config.c_bbsuid) != 0) {
986 important_message("Citadel/UX Setup",
987 "Failed to change the user ID to your BBS user.");
992 /***** begin version update section ***** */
993 /* take care of any updating that is necessary */
995 old_setup_level = config.c_setup_level;
997 if (old_setup_level == 0) goto NEW_INST;
999 if (old_setup_level < 323) {
1000 important_message("Citadel/UX Setup",
1001 "This Citadel/UX installation is too old to be upgraded.");
1005 write_config_to_disk();
1007 if ((config.c_setup_level / 10) == 32) {
1008 important_msgnum(31);
1012 if (config.c_setup_level < 400) {
1013 config.c_setup_level = 400;
1016 /* end of 3.23 -> 4.00 update section */
1018 /* end of 4.00 -> 4.02 update section */
1020 old_setup_level = config.c_setup_level;
1022 /* end of version update section */
1025 config.c_setup_level = REV_LEVEL;
1026 write_config_to_disk();
1028 system("mkdir info 2>/dev/null"); /* Create these */
1029 system("mkdir bio 2>/dev/null");
1030 system("mkdir userpics 2>/dev/null");
1031 system("mkdir messages 2>/dev/null");
1032 system("mkdir help 2>/dev/null");
1033 system("mkdir images 2>/dev/null");
1034 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1038 system("rm -fr ./chatpipes 2>/dev/null"); /* Don't need these */
1039 system("rm -fr ./expressmsgs 2>/dev/null");
1042 check_services_entry(); /* Check /etc/services */
1043 check_inittab_entry(); /* Check /etc/inittab */
1045 progress("Setting file permissions", 0, 3);
1046 chown(".", config.c_bbsuid, getgid());
1047 progress("Setting file permissions", 1, 3);
1048 chown("citadel.config", config.c_bbsuid, getgid());
1049 progress("Setting file permissions", 2, 3);
1050 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1053 progress("Setting file permissions", 3, 3);
1055 important_message("Setup finished",
1056 "Setup is finished. You may now start the Citadel server.");