2 * Citadel/UX setup program
4 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
13 #include <sys/types.h>
15 #include <sys/utsname.h>
34 #define UI_TEXT 0 /* Default setup type -- text only */
35 #define UI_DIALOG 1 /* Use the 'dialog' program */
36 #define UI_CURSES 2 /* Use curses */
38 #define SERVICE_NAME "citadel"
39 #define PROTO_NAME "tcp"
42 char setup_directory[128];
45 char *setup_titles[] = {
48 "Fully Qualified Domain Name (FQDN)",
49 "Human-readable node name",
52 "System Administrator",
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",
63 "Name of bit bucket subdirectory",
64 "System net password",
69 char *setup_text[] = {
72 "Enter the full pathname of the directory in which the BBS you are",
73 "creating or updating resides. If you specify a directory other than the",
74 "default, you will need to specify the -h flag to the server when you start",
78 "This is the name your system is known by on a Citadel/UX network. It",
79 "should be 8 characters or less, and should generally be comprised only of",
80 "letters. You can also use numbers and hyphens if necessary.",
83 "This is the name your system is known by on the Internet.",
84 "If you're not on the Internet, simply set this to your",
85 "node name followed by '.UUCP'.",
88 "This is a longer description of your system, readable by",
89 "us mere humans. It can be up to 20 characters long and it",
90 "can have spaces in it. Note that if you are part of a",
91 "Cit86Net, this is the name your system will be known by on",
95 "This is the main dialup number for your system. If yours",
96 "can not be dialed into, then make one up! It should be in",
97 "the format 'US 000 000 0000' - the US is your country code",
98 "(look it up if you're not in the United States) and the",
99 "rest is, of course, your area code and phone number.",
100 "This doesn't have any use in Citadel/UX, but gateways to",
101 "other networks may require it, and someday we may use this",
102 "to have the networker automatically build a BBS list.",
105 "Enter the geographical location of your system (city and",
106 "state/province/country etc.)",
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 "This is a boolean value. If you set it to 1, anyone who",
120 "creates a class 3 (passworded) or class 4 (invitation",
121 "only) room will automatically become the Room Aide for",
122 "that room, allowing them to edit it, delete/move messages,",
123 "etc. This is an administrative decision: it works well on",
124 "some systems, and not so well on others. Set this to 0 to",
125 "disable this function.",
128 "This setting specifies how long a server session may sit idle before it is",
129 "automatically terminated. The recommended value is 900 seconds (15",
130 "minutes). Note that this has *nothing* to do with any watchdog timer that",
131 "is presented to the user. The server's timeout is intended to kill idle or",
132 "zombie sessions running on a network, etc. ",
133 "You MUST set this to a reasonable value. Setting it to zero will cause",
134 "the server to malfunction.",
137 "This is the access level new users are assigned.",
139 "The most common settings for this will be either 1, for",
140 "systems which require new user validation by the system",
141 "administrator, 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.",
228 struct config config;
231 void cleanup(int exitcode) {
233 if (setup_type == UI_CURSES) {
240 /* Do an 'init q' if we need to. When we hit the right one, init
241 * will take over and setup won't come back because we didn't do a
242 * fork(). If init isn't found, we fall through the bottom of the
243 * loop and setup exits quietly.
246 execlp("/sbin/init", "init", "q", NULL);
247 execlp("/usr/sbin/init", "init", "q", NULL);
248 execlp("/bin/init", "init", "q", NULL);
249 execlp("/usr/bin/init", "init", "q", NULL);
250 execlp("init", "init", "q", NULL);
258 void getlin(int yp, int xp, char *string, int lim) /* Gets a line from the terminal */
259 /* Where on the screen to start */
260 /* Pointer to string buffer */
261 /* Maximum length - if negative, no-show */
266 if (lim<0) { lim=(0-lim); flag=1; }
269 for (a=0; a<lim; ++a) addch('-');
272 for (a=0; a<lim; ++a) addch(' ');
274 printw("%s", string);
275 GLA: move(yp,xp+strlen(string));
281 if ((a==8)&&(strlen(string)==0)) goto GLA;
282 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
283 if ((a==8)&&(string[0]!=0)) {
284 string[strlen(string)-1]=0;
285 move(yp,xp+strlen(string));
289 if ((a==13)||(a==10)) {
292 for (a=0; a<lim; ++a) addch(' ');
293 mvprintw(yp,xp,"%s",string);
300 if (flag==0) addch(a);
301 if (flag==1) addch('*');
308 void title(char *text)
310 if (setup_type == UI_TEXT) {
311 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
316 void hit_any_key(void) {
320 if (setup_type == UI_CURSES) {
321 mvprintw(20, 0, "Press any key to continue... ");
327 printf("Press return to continue...");
328 fgets(junk, 5, stdin);
331 int yesno(char *question)
340 printf("%s\nYes/No --> ",question);
341 fgets(buf, 4096, stdin);
342 answer=tolower(buf[0]);
343 if (answer=='y') answer=1;
344 else if (answer=='n') answer=0;
345 } while ((answer<0)||(answer>1));
349 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
350 answer = ( (system(buf)==0) ? 1 : 0);
357 mvprintw(1, 20, "Question");
359 mvprintw(10, 0, "%-80s", question);
360 mvprintw(20, 0, "%80s", "");
361 mvprintw(20, 0, "Yes/No -> ");
364 answer=tolower(answer);
365 if (answer=='y') answer=1;
366 else if (answer=='n') answer=0;
367 } while ((answer<0)||(answer>1));
377 void dump_access_levels(void) {
379 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
382 void get_setup_msg(char *dispbuf, int msgnum) {
387 while (atol(setup_text[a]) != msgnum) ++a;
391 strcat(dispbuf, setup_text[a++]);
392 strcat(dispbuf, "\n");
393 } while(atol(setup_text[a])!=(msgnum+1));
396 void print_setup(int msgnum) {
399 get_setup_msg(dispbuf, msgnum);
400 printf("\n\n%s\n\n", dispbuf);
404 void important_message(char *title, char *msgtext) {
410 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");
411 printf(" %s \n\n%s\n\n", title, msgtext);
416 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
425 printw(" Important Message ");
428 printw("%s", msgtext);
437 void important_msgnum(int msgnum) {
440 get_setup_msg(dispbuf, msgnum);
441 important_message("Important Message", dispbuf);
444 void display_error(char *error_message) {
445 important_message("Error", error_message);
448 void progress(char *text, long int curr, long int cmax)
450 static long dots_printed;
453 static FILE *gauge = NULL;
461 printf("..........................");
462 printf("..........................");
463 printf("..........................\r");
467 else if (curr==cmax) {
468 printf("\r%79s\n","");
471 a=(curr * 100) / cmax;
473 while (dots_printed < a) {
488 printf("..........................");
489 printf("..........................");
490 printf("..........................\r");
494 else if (curr==cmax) {
499 a=(curr * 100) / cmax;
503 while (dots_printed < a) {
513 if ( (curr == 0) && (gauge == NULL) ) {
514 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
516 gauge = (FILE *) popen(gcmd, "w");
519 else if (curr==cmax) {
520 fprintf(gauge, "100\n");
525 a=(curr * 100) / cmax;
527 fprintf(gauge, "%ld\n", a);
539 * check_services_entry() -- Make sure "citadel" is in /etc/services
542 void check_services_entry(void) {
547 "There is no '%s' entry in /etc/services. Would you like to add one?",
550 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
551 if (yesno(question)==1) {
552 sfp = fopen("/etc/services", "a");
554 display_error(strerror(errno));
557 fprintf(sfp, "%s 504/tcp\n",
568 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
571 void check_inittab_entry(void) {
574 char looking_for[256];
580 /* Determine the fully qualified path name of citserver */
581 sprintf(looking_for, "%s/citserver ", BBSDIR);
583 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
584 * an entry is found which we believe starts citserver.
586 infp = fopen("/etc/inittab", "r");
588 display_error(strerror(errno));
591 while (fgets(buf, 256, infp) != NULL) {
592 buf[strlen(buf) - 1] = 0;
593 ptr = strtok(buf, ":");
594 ptr = strtok(NULL, ":");
595 ptr = strtok(NULL, ":");
596 ptr = strtok(NULL, ":");
598 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
606 /* If there's already an entry, then we have nothing left to do. */
607 if (have_entry > 0) return;
609 /* Otherwise, prompt the user to create an entry. */
611 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
613 if (yesno(question)==0) return;
615 /* Generate a unique entry name for /etc/inittab */
616 sprintf(entryname, "c0");
619 if (entryname[1] > '9') {
622 if (entryname[0] > 'z') {
624 "Can't generate a unique entry name");
629 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
630 } while(system(buf)==0);
632 /* Now write it out to /etc/inittab */
633 infp = fopen("/etc/inittab", "a");
635 display_error(strerror(errno));
638 fprintf(infp, "# Start the Citadel/UX server...\n");
639 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
640 entryname, looking_for, setup_directory);
648 void set_str_val(int msgpos, char str[]) {
654 sprintf(tempfile, "/tmp/setup.%d", getpid());
656 switch (setup_type) {
658 title(setup_titles[msgpos]);
660 if (msgpos==11) dump_access_levels();
661 printf("This is currently set to:\n%s\n",str);
662 printf("Enter new value or press return to leave unchanged:\n");
663 fgets(buf, 4096, stdin);
664 buf[strlen(buf)-1] = 0;
665 if (strlen(buf)!=0) strcpy(str,buf);
668 get_setup_msg(setupmsg, msgpos);
670 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
671 setup_titles[msgpos],
674 if (system(buf)==0) {
675 fp = fopen(tempfile, "rb");
676 fgets(str, 4095, fp);
679 if (str[strlen(str)-1]==10)
680 str[strlen(str)-1]=0;
686 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
688 printw("%s", setup_titles[msgpos]);
691 get_setup_msg(setupmsg, msgpos);
692 printw("%s", setupmsg);
694 getlin(20, 0, str, 80);
700 void set_int_val(int msgpos, int *ip)
703 sprintf(buf,"%d",(int)*ip);
704 set_str_val(msgpos, buf);
709 void set_char_val(int msgpos, char *ip)
712 sprintf(buf,"%d",(int)*ip);
713 set_str_val(msgpos, buf);
714 *ip = (char)atoi(buf);
718 void set_long_val(int msgpos, long int *ip)
721 sprintf(buf,"%ld",*ip);
722 set_str_val(msgpos, buf);
727 void edit_value(int curr)
734 set_str_val(curr, config.c_nodename);
738 set_str_val(curr, config.c_fqdn);
742 set_str_val(curr, config.c_humannode);
746 set_str_val(curr, config.c_phonenum);
750 set_str_val(curr, config.c_bbs_city);
754 set_str_val(curr, config.c_sysadm);
758 set_int_val(curr, &config.c_bbsuid);
762 set_char_val(curr, &config.c_creataide);
766 set_int_val(curr, &config.c_sleeping);
770 set_char_val(curr, &config.c_initax);
774 set_char_val(curr, &config.c_regiscall);
778 set_char_val(curr, &config.c_twitdetect);
782 set_str_val(curr, config.c_twitroom);
786 set_int_val(curr, &config.c_maxsessions);
790 set_str_val(curr, config.c_moreprompt);
794 set_char_val(curr, &config.c_restrict);
798 set_str_val(curr, config.c_bucket_dir);
799 config.c_bucket_dir[14] = 0;
800 for (a=0; a<strlen(config.c_bucket_dir); ++a)
801 if (!isalpha(config.c_bucket_dir[a]))
802 strcpy(&config.c_bucket_dir[a],
803 &config.c_bucket_dir[a+1]);
807 set_str_val(curr, config.c_net_password);
811 set_int_val(curr, &config.c_port_number);
819 * (re-)write the config data to disk
821 void write_config_to_disk(void) {
824 fp=fopen("citadel.config","wb");
826 display_error("setup: cannot open citadel.config");
829 fwrite((char *)&config,sizeof(struct config),1,fp);
837 * Figure out what type of user interface we're going to use
839 int discover_ui(void) {
845 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
856 int main(int argc, char *argv[]) {
861 int old_setup_level = 0;
863 struct utsname my_utsname;
865 /* set an invalid setup type */
868 /* parse command line args */
869 for (a=0; a<argc; ++a) {
870 if (!strncmp(argv[a], "-u", 2)) {
871 strcpy(aaa, argv[a]);
872 strcpy(aaa, &aaa[2]);
873 setup_type = atoi(aaa);
875 if (!strcmp(argv[a], "-i")) {
881 /* If a setup type was not specified, try to determine automatically
882 * the best one to use out of all available types.
884 if (setup_type < 0) {
885 setup_type = discover_ui();
889 if (setup_type == UI_CURSES) {
896 if (info_only == 1) {
897 important_message("Citadel/UX Setup", CITADEL);
901 /* Get started in a valid setup directory. */
902 strcpy(setup_directory, BBSDIR);
903 set_str_val(0, setup_directory);
904 if (chdir(setup_directory) != 0) {
905 important_message("Citadel/UX Setup",
906 "The directory you specified does not exist.");
910 /* Determine our host name, in case we need to use it as a default */
917 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
921 system("exec clear");
927 * What we're going to try to do here is append a whole bunch of
928 * nulls to the citadel.config file, so we can keep the old config
929 * values if they exist, but if the file is missing or from an
930 * earlier version with a shorter config structure, when setup tries
931 * to read the old config parameters, they'll all come up zero.
932 * The length of the config file will be set to what it's supposed
933 * to be when we rewrite it, because we replace the old file with a
934 * completely new copy. (Neat, eh?)
937 fp=fopen("citadel.config","ab");
939 display_error("setup: cannot append citadel.config");
942 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
945 /* now we re-open it, and read the old or blank configuration */
946 fp=fopen("citadel.config","rb");
948 display_error("setup: cannot open citadel.config");
951 fread((char *)&config,sizeof(struct config),1,fp);
955 /* set some sample/default values in place of blanks... */
956 if (strlen(config.c_nodename)==0)
957 strcpy(config.c_nodename, my_utsname.nodename);
958 if (strlen(config.c_fqdn)==0)
959 sprintf(config.c_fqdn, "%s.citadelia.org", my_utsname.nodename);
960 if (strlen(config.c_humannode)==0)
961 strcpy(config.c_humannode,"My System");
962 if (strlen(config.c_phonenum)==0)
963 strcpy(config.c_phonenum,"US 800 555 1212");
964 if (config.c_initax == 0)
966 if (strlen(config.c_moreprompt)==0)
967 strcpy(config.c_moreprompt,"<more>");
968 if (strlen(config.c_twitroom)==0)
969 strcpy(config.c_twitroom,"Trashcan");
970 if (strlen(config.c_bucket_dir)==0)
971 strcpy(config.c_bucket_dir,"bitbucket");
972 if (strlen(config.c_net_password)==0)
973 strcpy(config.c_net_password,"netpassword");
974 if (config.c_port_number == 0) {
975 config.c_port_number = 504;
977 if (config.c_ipgm_secret == 0) {
979 config.c_ipgm_secret = rand();
981 if (config.c_sleeping == 0) {
982 config.c_sleeping = 900;
985 /* We need a system default message expiry policy, because this is
986 * the top level and there's no 'higher' policy to fall back on.
988 if (config.c_ep.expire_mode == 0) {
989 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
990 config.c_ep.expire_value = 150;
993 /* Go through a series of dialogs prompting for config info */
994 for (curr = 1; curr <= MAXSETUP; ++curr) {
999 if (setuid(config.c_bbsuid) != 0) {
1000 important_message("Citadel/UX Setup",
1001 "Failed to change the user ID to your BBS user.");
1006 /***** begin version update section ***** */
1007 /* take care of any updating that is necessary */
1009 old_setup_level = config.c_setup_level;
1011 if (old_setup_level == 0) goto NEW_INST;
1013 if (old_setup_level < 323) {
1014 important_message("Citadel/UX Setup",
1015 "This Citadel/UX installation is too old to be upgraded.");
1019 write_config_to_disk();
1021 if ((config.c_setup_level / 10) == 32) {
1022 important_msgnum(31);
1026 if (config.c_setup_level < 400) {
1027 config.c_setup_level = 400;
1030 /* end of 3.23 -> 4.00 update section */
1032 /* end of 4.00 -> 4.02 update section */
1034 old_setup_level = config.c_setup_level;
1036 /* end of version update section */
1039 config.c_setup_level = REV_LEVEL;
1041 /******************************************/
1043 write_config_to_disk();
1045 system("mkdir info 2>/dev/null"); /* Create these */
1046 system("mkdir bio 2>/dev/null");
1047 system("mkdir userpics 2>/dev/null");
1048 system("mkdir messages 2>/dev/null");
1049 system("mkdir help 2>/dev/null");
1050 system("mkdir images 2>/dev/null");
1051 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
1054 /* Delete a bunch of old files from Citadel v4; don't need anymore */
1055 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
1057 check_services_entry(); /* Check /etc/services */
1058 check_inittab_entry(); /* Check /etc/inittab */
1060 progress("Setting file permissions", 0, 3);
1061 chown(".", config.c_bbsuid, getgid());
1062 progress("Setting file permissions", 1, 3);
1063 chown("citadel.config", config.c_bbsuid, getgid());
1064 progress("Setting file permissions", 2, 3);
1065 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
1068 progress("Setting file permissions", 3, 3);
1070 important_message("Setup finished",
1071 "Setup is finished. You may now start the Citadel server.");