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[] = {
49 "System Administrator",
51 "Name of bit bucket subdirectory",
56 char *setup_text[] = {
59 "Enter the full pathname of the directory in which the BBS you are",
60 "creating or updating resides. If you specify a directory other than the",
61 "default, you will need to specify the -h flag to the server when you start",
65 "Enter the name of the system administrator (which is probably you).",
66 "When an account is created with this name, it will automatically be",
67 "assigned the highest access level.",
70 "You should create a user called 'bbs', 'guest', 'citadel', or something",
71 "similar, that will allow users a way into your BBS. The server will run",
72 "under this user ID. Please specify that (numeric) user ID here.",
75 "Select the name of a subdirectory (relative to the main",
76 "Citadel directory - do not type an absolute pathname!) in",
77 "which to place arriving file transfers that otherwise",
81 "Specify the TCP port number on which your server will run. Normally, this",
82 "will be port 504, which is the official port assigned by the IANA for",
83 "Citadel servers. You'll only need to specify a different port number if",
84 "you run multiple BBS's on the same computer and there's something else",
85 "already using port 504.",
115 "Setup has detected that you currently have data files from a Citadel/UX",
116 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
117 "files to version 4.0x format.",
118 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
119 "files, and run setup again.",
125 struct config config;
128 void cleanup(int exitcode) {
130 if (setup_type == UI_CURSES) {
137 /* Do an 'init q' if we need to. When we hit the right one, init
138 * will take over and setup won't come back because we didn't do a
139 * fork(). If init isn't found, we fall through the bottom of the
140 * loop and setup exits quietly.
143 execlp("/sbin/init", "init", "q", NULL);
144 execlp("/usr/sbin/init", "init", "q", NULL);
145 execlp("/bin/init", "init", "q", NULL);
146 execlp("/usr/bin/init", "init", "q", NULL);
147 execlp("init", "init", "q", NULL);
155 void getlin(int yp, int xp, char *string, int lim) /* Gets a line from the terminal */
156 /* Where on the screen to start */
157 /* Pointer to string buffer */
158 /* Maximum length - if negative, no-show */
163 if (lim<0) { lim=(0-lim); flag=1; }
166 for (a=0; a<lim; ++a) addch('-');
169 for (a=0; a<lim; ++a) addch(' ');
171 printw("%s", string);
172 GLA: move(yp,xp+strlen(string));
178 if ((a==8)&&(strlen(string)==0)) goto GLA;
179 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
180 if ((a==8)&&(string[0]!=0)) {
181 string[strlen(string)-1]=0;
182 move(yp,xp+strlen(string));
186 if ((a==13)||(a==10)) {
189 for (a=0; a<lim; ++a) addch(' ');
190 mvprintw(yp,xp,"%s",string);
197 if (flag==0) addch(a);
198 if (flag==1) addch('*');
205 void title(char *text)
207 if (setup_type == UI_TEXT) {
208 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
213 void hit_any_key(void) {
217 if (setup_type == UI_CURSES) {
218 mvprintw(20, 0, "Press any key to continue... ");
224 printf("Press return to continue...");
225 fgets(junk, 5, stdin);
228 int yesno(char *question)
237 printf("%s\nYes/No --> ",question);
238 fgets(buf, 4096, stdin);
239 answer=tolower(buf[0]);
240 if (answer=='y') answer=1;
241 else if (answer=='n') answer=0;
242 } while ((answer<0)||(answer>1));
246 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
247 answer = ( (system(buf)==0) ? 1 : 0);
254 mvprintw(1, 20, "Question");
256 mvprintw(10, 0, "%-80s", question);
257 mvprintw(20, 0, "%80s", "");
258 mvprintw(20, 0, "Yes/No -> ");
261 answer=tolower(answer);
262 if (answer=='y') answer=1;
263 else if (answer=='n') answer=0;
264 } while ((answer<0)||(answer>1));
274 void dump_access_levels(void) {
276 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
279 void get_setup_msg(char *dispbuf, int msgnum) {
284 while (atol(setup_text[a]) != msgnum) ++a;
288 strcat(dispbuf, setup_text[a++]);
289 strcat(dispbuf, "\n");
290 } while(atol(setup_text[a])!=(msgnum+1));
293 void print_setup(int msgnum) {
296 get_setup_msg(dispbuf, msgnum);
297 printf("\n\n%s\n\n", dispbuf);
301 void important_message(char *title, char *msgtext) {
307 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");
308 printf(" %s \n\n%s\n\n", title, msgtext);
313 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
322 printw(" Important Message ");
325 printw("%s", msgtext);
334 void important_msgnum(int msgnum) {
337 get_setup_msg(dispbuf, msgnum);
338 important_message("Important Message", dispbuf);
341 void display_error(char *error_message) {
342 important_message("Error", error_message);
345 void progress(char *text, long int curr, long int cmax)
347 static long dots_printed;
350 static FILE *gauge = NULL;
358 printf("..........................");
359 printf("..........................");
360 printf("..........................\r");
364 else if (curr==cmax) {
365 printf("\r%79s\n","");
368 a=(curr * 100) / cmax;
370 while (dots_printed < a) {
385 printf("..........................");
386 printf("..........................");
387 printf("..........................\r");
391 else if (curr==cmax) {
396 a=(curr * 100) / cmax;
400 while (dots_printed < a) {
410 if ( (curr == 0) && (gauge == NULL) ) {
411 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
413 gauge = (FILE *) popen(gcmd, "w");
416 else if (curr==cmax) {
417 fprintf(gauge, "100\n");
422 a=(curr * 100) / cmax;
424 fprintf(gauge, "%ld\n", a);
436 * check_services_entry() -- Make sure "citadel" is in /etc/services
439 void check_services_entry(void) {
444 "There is no '%s' entry in /etc/services. Would you like to add one?",
447 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
448 if (yesno(question)==1) {
449 sfp = fopen("/etc/services", "a");
451 display_error(strerror(errno));
454 fprintf(sfp, "%s 504/tcp\n",
465 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
468 void check_inittab_entry(void) {
471 char looking_for[256];
477 /* Determine the fully qualified path name of citserver */
478 sprintf(looking_for, "%s/citserver ", BBSDIR);
480 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
481 * an entry is found which we believe starts citserver.
483 infp = fopen("/etc/inittab", "r");
485 display_error(strerror(errno));
488 while (fgets(buf, 256, infp) != NULL) {
489 buf[strlen(buf) - 1] = 0;
490 ptr = strtok(buf, ":");
491 ptr = strtok(NULL, ":");
492 ptr = strtok(NULL, ":");
493 ptr = strtok(NULL, ":");
495 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
503 /* If there's already an entry, then we have nothing left to do. */
504 if (have_entry > 0) return;
506 /* Otherwise, prompt the user to create an entry. */
508 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
510 if (yesno(question)==0) return;
512 /* Generate a unique entry name for /etc/inittab */
513 sprintf(entryname, "c0");
516 if (entryname[1] > '9') {
519 if (entryname[0] > 'z') {
521 "Can't generate a unique entry name");
526 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
527 } while(system(buf)==0);
529 /* Now write it out to /etc/inittab */
530 infp = fopen("/etc/inittab", "a");
532 display_error(strerror(errno));
535 fprintf(infp, "# Start the Citadel/UX server...\n");
536 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
537 entryname, looking_for, setup_directory);
545 void set_str_val(int msgpos, char str[]) {
551 sprintf(tempfile, "/tmp/setup.%d", getpid());
553 switch (setup_type) {
555 title(setup_titles[msgpos]);
557 if (msgpos==11) dump_access_levels();
558 printf("This is currently set to:\n%s\n",str);
559 printf("Enter new value or press return to leave unchanged:\n");
560 fgets(buf, 4096, stdin);
561 buf[strlen(buf)-1] = 0;
562 if (strlen(buf)!=0) strcpy(str,buf);
565 get_setup_msg(setupmsg, msgpos);
567 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
568 setup_titles[msgpos],
571 if (system(buf)==0) {
572 fp = fopen(tempfile, "rb");
573 fgets(str, 4095, fp);
576 if (str[strlen(str)-1]==10)
577 str[strlen(str)-1]=0;
583 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
585 printw("%s", setup_titles[msgpos]);
588 get_setup_msg(setupmsg, msgpos);
589 printw("%s", setupmsg);
591 getlin(20, 0, str, 80);
597 void set_int_val(int msgpos, int *ip)
600 sprintf(buf,"%d",(int)*ip);
601 set_str_val(msgpos, buf);
606 void set_char_val(int msgpos, char *ip)
609 sprintf(buf,"%d",(int)*ip);
610 set_str_val(msgpos, buf);
611 *ip = (char)atoi(buf);
615 void set_long_val(int msgpos, long int *ip)
618 sprintf(buf,"%ld",*ip);
619 set_str_val(msgpos, buf);
624 void edit_value(int curr)
631 set_str_val(curr, config.c_sysadm);
635 set_int_val(curr, &config.c_bbsuid);
639 set_str_val(curr, config.c_bucket_dir);
640 config.c_bucket_dir[14] = 0;
641 for (a=0; a<strlen(config.c_bucket_dir); ++a)
642 if (!isalpha(config.c_bucket_dir[a]))
643 strcpy(&config.c_bucket_dir[a],
644 &config.c_bucket_dir[a+1]);
648 set_int_val(curr, &config.c_port_number);
656 * (re-)write the config data to disk
658 void write_config_to_disk(void) {
662 if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
663 display_error("setup: cannot open citadel.config");
669 display_error("setup: cannot open citadel.config");
672 fwrite((char *)&config,sizeof(struct config),1,fp);
680 * Figure out what type of user interface we're going to use
682 int discover_ui(void) {
688 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
699 int main(int argc, char *argv[]) {
704 int old_setup_level = 0;
706 struct utsname my_utsname;
709 /* set an invalid setup type */
712 /* parse command line args */
713 for (a=0; a<argc; ++a) {
714 if (!strncmp(argv[a], "-u", 2)) {
715 strcpy(aaa, argv[a]);
716 strcpy(aaa, &aaa[2]);
717 setup_type = atoi(aaa);
719 if (!strcmp(argv[a], "-i")) {
725 /* If a setup type was not specified, try to determine automatically
726 * the best one to use out of all available types.
728 if (setup_type < 0) {
729 setup_type = discover_ui();
733 if (setup_type == UI_CURSES) {
740 if (info_only == 1) {
741 important_message("Citadel/UX Setup", CITADEL);
745 /* Get started in a valid setup directory. */
746 strcpy(setup_directory, BBSDIR);
747 set_str_val(0, setup_directory);
748 if (chdir(setup_directory) != 0) {
749 important_message("Citadel/UX Setup",
750 "The directory you specified does not exist.");
754 /* Determine our host name, in case we need to use it as a default */
761 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
765 system("exec clear");
771 * What we're going to try to do here is append a whole bunch of
772 * nulls to the citadel.config file, so we can keep the old config
773 * values if they exist, but if the file is missing or from an
774 * earlier version with a shorter config structure, when setup tries
775 * to read the old config parameters, they'll all come up zero.
776 * The length of the config file will be set to what it's supposed
777 * to be when we rewrite it, because we replace the old file with a
778 * completely new copy. (Neat, eh?)
781 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
782 S_IRUSR | S_IWUSR)) == -1) {
783 display_error("setup: cannot append citadel.config");
789 display_error("setup: cannot append citadel.config");
792 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
795 /* now we re-open it, and read the old or blank configuration */
796 fp=fopen("citadel.config","rb");
798 display_error("setup: cannot open citadel.config");
801 fread((char *)&config,sizeof(struct config),1,fp);
805 /* set some sample/default values in place of blanks... */
806 if (strlen(config.c_nodename)==0)
807 strcpy(config.c_nodename, my_utsname.nodename);
808 if (strlen(config.c_fqdn)==0)
809 sprintf(config.c_fqdn, "%s.local", my_utsname.nodename);
810 if (strlen(config.c_humannode)==0)
811 strcpy(config.c_humannode,"My System");
812 if (strlen(config.c_phonenum)==0)
813 strcpy(config.c_phonenum,"US 800 555 1212");
814 if (config.c_initax == 0)
816 if (strlen(config.c_moreprompt)==0)
817 strcpy(config.c_moreprompt,"<more>");
818 if (strlen(config.c_twitroom)==0)
819 strcpy(config.c_twitroom,"Trashcan");
820 if (strlen(config.c_bucket_dir)==0)
821 strcpy(config.c_bucket_dir,"bitbucket");
822 if (strlen(config.c_net_password)==0)
823 strcpy(config.c_net_password,"netpassword");
824 if (config.c_port_number == 0) {
825 config.c_port_number = 504;
827 if (config.c_ipgm_secret == 0) {
829 config.c_ipgm_secret = rand();
831 if (config.c_sleeping == 0) {
832 config.c_sleeping = 900;
834 if (config.c_bbsuid == 0) {
835 pw = getpwnam("citadel");
836 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
838 if (config.c_bbsuid == 0) {
839 pw = getpwnam("bbs");
840 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
842 if (config.c_bbsuid == 0) {
843 pw = getpwnam("guest");
844 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
848 * Make sure that at least one concurrent session is allowed!
850 if (config.c_maxsessions < 1) {
851 config.c_maxsessions = 1;
854 /* We need a system default message expiry policy, because this is
855 * the top level and there's no 'higher' policy to fall back on.
857 if (config.c_ep.expire_mode == 0) {
858 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
859 config.c_ep.expire_value = 150;
862 /* Go through a series of dialogs prompting for config info */
863 for (curr = 1; curr <= MAXSETUP; ++curr) {
868 if (setuid(config.c_bbsuid) != 0) {
869 important_message("Citadel/UX Setup",
870 "Failed to change the user ID to your BBS user.");
875 /***** begin version update section ***** */
876 /* take care of any updating that is necessary */
878 old_setup_level = config.c_setup_level;
880 if (old_setup_level == 0) goto NEW_INST;
882 if (old_setup_level < 323) {
883 important_message("Citadel/UX Setup",
884 "This Citadel/UX installation is too old to be upgraded.");
888 write_config_to_disk();
890 if ((config.c_setup_level / 10) == 32) {
891 important_msgnum(31);
895 if (config.c_setup_level < 400) {
896 config.c_setup_level = 400;
899 /* end of 3.23 -> 4.00 update section */
901 /* end of 4.00 -> 4.02 update section */
903 old_setup_level = config.c_setup_level;
905 /* end of version update section */
908 config.c_setup_level = REV_LEVEL;
910 /******************************************/
912 write_config_to_disk();
914 system("mkdir info 2>/dev/null"); /* Create these */
915 system("mkdir bio 2>/dev/null");
916 system("mkdir userpics 2>/dev/null");
917 system("mkdir messages 2>/dev/null");
918 system("mkdir help 2>/dev/null");
919 system("mkdir images 2>/dev/null");
920 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
923 /* Delete a bunch of old files from Citadel v4; don't need anymore */
924 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
926 check_services_entry(); /* Check /etc/services */
927 check_inittab_entry(); /* Check /etc/inittab */
929 progress("Setting file permissions", 0, 3);
930 chown(".", config.c_bbsuid, getgid());
931 progress("Setting file permissions", 1, 3);
932 chown("citadel.config", config.c_bbsuid, getgid());
933 progress("Setting file permissions", 2, 3);
934 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
937 progress("Setting file permissions", 3, 3);
939 important_message("Setup finished",
940 "Setup is finished. You may now start the Citadel server.");