2 * Citadel/UX setup program
5 * *** YOU MUST EDIT sysconfig.h >BEFORE< COMPILING SETUP ***
14 #include <sys/types.h>
16 #include <sys/utsname.h>
37 #define UI_TEXT 0 /* Default setup type -- text only */
38 #define UI_DIALOG 1 /* Use the 'dialog' program */
39 #define UI_CURSES 2 /* Use curses */
41 #define SERVICE_NAME "citadel"
42 #define PROTO_NAME "tcp"
45 char setup_directory[128];
48 char *setup_titles[] = {
50 "System Administrator",
52 "Name of bit bucket subdirectory",
57 char *setup_text[] = {
60 "Enter the full pathname of the directory in which the BBS you are",
61 "creating or updating resides. If you specify a directory other than the",
62 "default, you will need to specify the -h flag to the server when you start",
66 "Enter the name of the system administrator (which is probably you).",
67 "When an account is created with this name, it will automatically be",
68 "assigned the highest access level.",
71 "You should create a user called 'bbs', 'guest', 'citadel', or something",
72 "similar, that will allow users a way into your BBS. The server will run",
73 "under this user ID. Please specify that (numeric) user ID here.",
76 "Select the name of a subdirectory (relative to the main",
77 "Citadel directory - do not type an absolute pathname!) in",
78 "which to place arriving file transfers that otherwise",
82 "Specify the TCP port number on which your server will run. Normally, this",
83 "will be port 504, which is the official port assigned by the IANA for",
84 "Citadel servers. You'll only need to specify a different port number if",
85 "you run multiple BBS's on the same computer and there's something else",
86 "already using port 504.",
116 "Setup has detected that you currently have data files from a Citadel/UX",
117 "version 3.2x installation. The program 'conv_32_40' can upgrade your",
118 "files to version 4.0x format.",
119 " Setup will now exit. Please either run 'conv_32_40' or delete your data",
120 "files, and run setup again.",
126 struct config config;
129 void cleanup(int exitcode) {
131 if (setup_type == UI_CURSES) {
138 /* Do an 'init q' if we need to. When we hit the right one, init
139 * will take over and setup won't come back because we didn't do a
140 * fork(). If init isn't found, we fall through the bottom of the
141 * loop and setup exits quietly.
144 execlp("/sbin/init", "init", "q", NULL);
145 execlp("/usr/sbin/init", "init", "q", NULL);
146 execlp("/bin/init", "init", "q", NULL);
147 execlp("/usr/bin/init", "init", "q", NULL);
148 execlp("init", "init", "q", NULL);
156 void getlin(int yp, int xp, char *string, int lim) /* Gets a line from the terminal */
157 /* Where on the screen to start */
158 /* Pointer to string buffer */
159 /* Maximum length - if negative, no-show */
164 if (lim<0) { lim=(0-lim); flag=1; }
167 for (a=0; a<lim; ++a) addch('-');
170 for (a=0; a<lim; ++a) addch(' ');
172 printw("%s", string);
173 GLA: move(yp,xp+strlen(string));
179 if ((a==8)&&(strlen(string)==0)) goto GLA;
180 if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
181 if ((a==8)&&(string[0]!=0)) {
182 string[strlen(string)-1]=0;
183 move(yp,xp+strlen(string));
187 if ((a==13)||(a==10)) {
190 for (a=0; a<lim; ++a) addch(' ');
191 mvprintw(yp,xp,"%s",string);
198 if (flag==0) addch(a);
199 if (flag==1) addch('*');
206 void title(char *text)
208 if (setup_type == UI_TEXT) {
209 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n",text);
214 void hit_any_key(void) {
218 if (setup_type == UI_CURSES) {
219 mvprintw(20, 0, "Press any key to continue... ");
225 printf("Press return to continue...");
226 fgets(junk, 5, stdin);
229 int yesno(char *question)
238 printf("%s\nYes/No --> ",question);
239 fgets(buf, 4096, stdin);
240 answer=tolower(buf[0]);
241 if (answer=='y') answer=1;
242 else if (answer=='n') answer=0;
243 } while ((answer<0)||(answer>1));
247 sprintf(buf, "dialog --yesno \"%s\" 7 80", question);
248 answer = ( (system(buf)==0) ? 1 : 0);
255 mvprintw(1, 20, "Question");
257 mvprintw(10, 0, "%-80s", question);
258 mvprintw(20, 0, "%80s", "");
259 mvprintw(20, 0, "Yes/No -> ");
262 answer=tolower(answer);
263 if (answer=='y') answer=1;
264 else if (answer=='n') answer=0;
265 } while ((answer<0)||(answer>1));
275 void dump_access_levels(void) {
277 for (a=0; a<=6; ++a) printf("%d %s\n",a,axdefs[a]);
280 void get_setup_msg(char *dispbuf, int msgnum) {
285 while (atol(setup_text[a]) != msgnum) ++a;
289 strcat(dispbuf, setup_text[a++]);
290 strcat(dispbuf, "\n");
291 } while(atol(setup_text[a])!=(msgnum+1));
294 void print_setup(int msgnum) {
297 get_setup_msg(dispbuf, msgnum);
298 printf("\n\n%s\n\n", dispbuf);
302 void important_message(char *title, char *msgtext) {
308 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");
309 printf(" %s \n\n%s\n\n", title, msgtext);
314 sprintf(buf, "dialog --title \"%s\" --msgbox \"\n%s\" 20 80",
323 printw(" Important Message ");
326 printw("%s", msgtext);
335 void important_msgnum(int msgnum) {
338 get_setup_msg(dispbuf, msgnum);
339 important_message("Important Message", dispbuf);
342 void display_error(char *error_message) {
343 important_message("Error", error_message);
346 void progress(char *text, long int curr, long int cmax)
348 static long dots_printed;
351 static FILE *gauge = NULL;
359 printf("..........................");
360 printf("..........................");
361 printf("..........................\r");
365 else if (curr==cmax) {
366 printf("\r%79s\n","");
369 a=(curr * 100) / cmax;
371 while (dots_printed < a) {
386 printf("..........................");
387 printf("..........................");
388 printf("..........................\r");
392 else if (curr==cmax) {
397 a=(curr * 100) / cmax;
401 while (dots_printed < a) {
411 if ( (curr == 0) && (gauge == NULL) ) {
412 sprintf(gcmd, "dialog --guage \"%s\" 7 80 0",
414 gauge = (FILE *) popen(gcmd, "w");
417 else if (curr==cmax) {
418 fprintf(gauge, "100\n");
423 a=(curr * 100) / cmax;
425 fprintf(gauge, "%ld\n", a);
437 * check_services_entry() -- Make sure "citadel" is in /etc/services
440 void check_services_entry(void) {
445 "There is no '%s' entry in /etc/services. Would you like to add one?",
448 if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
449 if (yesno(question)==1) {
450 sfp = fopen("/etc/services", "a");
452 display_error(strerror(errno));
455 fprintf(sfp, "%s 504/tcp\n",
466 * check_inittab_entry() -- Make sure "citadel" is in /etc/inittab
469 void check_inittab_entry(void) {
472 char looking_for[256];
478 /* Determine the fully qualified path name of citserver */
479 sprintf(looking_for, "%s/citserver ", BBSDIR);
481 /* Pound through /etc/inittab line by line. Set have_entry to 1 if
482 * an entry is found which we believe starts citserver.
484 infp = fopen("/etc/inittab", "r");
489 while (fgets(buf, 256, infp) != NULL) {
490 buf[strlen(buf) - 1] = 0;
491 ptr = strtok(buf, ":");
492 ptr = strtok(NULL, ":");
493 ptr = strtok(NULL, ":");
494 ptr = strtok(NULL, ":");
496 if (!strncmp(ptr, looking_for, strlen(looking_for))) {
504 /* If there's already an entry, then we have nothing left to do. */
505 if (have_entry > 0) return;
507 /* Otherwise, prompt the user to create an entry. */
509 "There is no '%s' entry in /etc/inittab.\nWould you like to add one?",
511 if (yesno(question)==0) return;
513 /* Generate a unique entry name for /etc/inittab */
514 sprintf(entryname, "c0");
517 if (entryname[1] > '9') {
520 if (entryname[0] > 'z') {
522 "Can't generate a unique entry name");
527 "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
528 } while(system(buf)==0);
530 /* Now write it out to /etc/inittab */
531 infp = fopen("/etc/inittab", "a");
533 display_error(strerror(errno));
536 fprintf(infp, "# Start the Citadel/UX server...\n");
537 fprintf(infp,"%s:2345:respawn:%s -h%s\n",
538 entryname, looking_for, setup_directory);
546 void set_str_val(int msgpos, char str[]) {
552 sprintf(tempfile, "/tmp/setup.%ld", (long)getpid());
554 switch (setup_type) {
556 title(setup_titles[msgpos]);
558 if (msgpos==11) dump_access_levels();
559 printf("This is currently set to:\n%s\n",str);
560 printf("Enter new value or press return to leave unchanged:\n");
561 fgets(buf, 4096, stdin);
562 buf[strlen(buf)-1] = 0;
563 if (strlen(buf)!=0) strcpy(str,buf);
566 get_setup_msg(setupmsg, msgpos);
568 "dialog --title \"%s\" --inputbox \"\n%s\n\" 20 80 \"%s\" 2>%s",
569 setup_titles[msgpos],
572 if (system(buf)==0) {
573 fp = fopen(tempfile, "rb");
574 fgets(str, 4095, fp);
577 if (str[strlen(str)-1]==10)
578 str[strlen(str)-1]=0;
584 move(1, ((80-strlen(setup_titles[msgpos]))/2) );
586 printw("%s", setup_titles[msgpos]);
589 get_setup_msg(setupmsg, msgpos);
590 printw("%s", setupmsg);
592 getlin(20, 0, str, 80);
598 void set_int_val(int msgpos, int *ip)
601 sprintf(buf,"%d",(int)*ip);
602 set_str_val(msgpos, buf);
607 void set_char_val(int msgpos, char *ip)
610 sprintf(buf,"%d",(int)*ip);
611 set_str_val(msgpos, buf);
612 *ip = (char)atoi(buf);
616 void set_long_val(int msgpos, long int *ip)
619 sprintf(buf,"%ld",*ip);
620 set_str_val(msgpos, buf);
625 void edit_value(int curr)
632 set_str_val(curr, config.c_sysadm);
636 set_int_val(curr, &config.c_bbsuid);
640 set_str_val(curr, config.c_bucket_dir);
641 config.c_bucket_dir[14] = 0;
642 for (a=0; a<strlen(config.c_bucket_dir); ++a)
643 if (!isalpha(config.c_bucket_dir[a]))
644 strcpy(&config.c_bucket_dir[a],
645 &config.c_bucket_dir[a+1]);
649 set_int_val(curr, &config.c_port_number);
657 * (re-)write the config data to disk
659 void write_config_to_disk(void) {
663 if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
664 display_error("setup: cannot open citadel.config");
670 display_error("setup: cannot open citadel.config");
673 fwrite((char *)&config,sizeof(struct config),1,fp);
681 * Figure out what type of user interface we're going to use
683 int discover_ui(void) {
689 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
700 int main(int argc, char *argv[]) {
705 int old_setup_level = 0;
707 struct utsname my_utsname;
712 /* set an invalid setup type */
715 /* parse command line args */
716 for (a=0; a<argc; ++a) {
717 if (!strncmp(argv[a], "-u", 2)) {
718 strcpy(aaa, argv[a]);
719 strcpy(aaa, &aaa[2]);
720 setup_type = atoi(aaa);
722 if (!strcmp(argv[a], "-i")) {
728 /* If a setup type was not specified, try to determine automatically
729 * the best one to use out of all available types.
731 if (setup_type < 0) {
732 setup_type = discover_ui();
736 if (setup_type == UI_CURSES) {
743 if (info_only == 1) {
744 important_message("Citadel/UX Setup", CITADEL);
748 /* Get started in a valid setup directory. */
749 strcpy(setup_directory, BBSDIR);
750 set_str_val(0, setup_directory);
751 if (chdir(setup_directory) != 0) {
752 important_message("Citadel/UX Setup",
753 "The directory you specified does not exist.");
757 /* Determine our host name, in case we need to use it as a default */
764 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
768 system("exec clear");
774 * What we're going to try to do here is append a whole bunch of
775 * nulls to the citadel.config file, so we can keep the old config
776 * values if they exist, but if the file is missing or from an
777 * earlier version with a shorter config structure, when setup tries
778 * to read the old config parameters, they'll all come up zero.
779 * The length of the config file will be set to what it's supposed
780 * to be when we rewrite it, because we replace the old file with a
781 * completely new copy. (Neat, eh?)
784 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
785 S_IRUSR | S_IWUSR)) == -1) {
786 display_error("setup: cannot append citadel.config");
792 display_error("setup: cannot append citadel.config");
795 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
798 /* now we re-open it, and read the old or blank configuration */
799 fp=fopen("citadel.config","rb");
801 display_error("setup: cannot open citadel.config");
804 fread((char *)&config,sizeof(struct config),1,fp);
808 /* set some sample/default values in place of blanks... */
809 if (strlen(config.c_nodename)==0)
810 safestrncpy(config.c_nodename, my_utsname.nodename,
811 sizeof config.c_nodename);
812 strtok(config.c_nodename, ".");
813 if (strlen(config.c_fqdn)==0) {
814 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
815 safestrncpy(config.c_fqdn, he->h_name,
816 sizeof config.c_fqdn);
818 safestrncpy(config.c_fqdn, my_utsname.nodename,
819 sizeof config.c_fqdn);
821 if (strlen(config.c_humannode)==0)
822 strcpy(config.c_humannode,"My System");
823 if (strlen(config.c_phonenum)==0)
824 strcpy(config.c_phonenum,"US 800 555 1212");
825 if (config.c_initax == 0)
827 if (strlen(config.c_moreprompt)==0)
828 strcpy(config.c_moreprompt,"<more>");
829 if (strlen(config.c_twitroom)==0)
830 strcpy(config.c_twitroom,"Trashcan");
831 if (strlen(config.c_bucket_dir)==0)
832 strcpy(config.c_bucket_dir,"bitbucket");
833 if (strlen(config.c_net_password)==0)
834 strcpy(config.c_net_password,"netpassword");
835 if (config.c_port_number == 0) {
836 config.c_port_number = 504;
838 if (config.c_ipgm_secret == 0) {
840 config.c_ipgm_secret = rand();
842 if (config.c_sleeping == 0) {
843 config.c_sleeping = 900;
845 if (config.c_bbsuid == 0) {
846 pw = getpwnam("citadel");
847 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
849 if (config.c_bbsuid == 0) {
850 pw = getpwnam("bbs");
851 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
853 if (config.c_bbsuid == 0) {
854 pw = getpwnam("guest");
855 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
859 * Negative values for maxsessions are not allowed.
861 if (config.c_maxsessions < 0) {
862 config.c_maxsessions = 0;
865 /* We need a system default message expiry policy, because this is
866 * the top level and there's no 'higher' policy to fall back on.
868 if (config.c_ep.expire_mode == 0) {
869 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
870 config.c_ep.expire_value = 150;
873 /* Go through a series of dialogs prompting for config info */
874 for (curr = 1; curr <= MAXSETUP; ++curr) {
879 if (setuid(config.c_bbsuid) != 0) {
880 important_message("Citadel/UX Setup",
881 "Failed to change the user ID to your BBS user.");
886 /***** begin version update section ***** */
887 /* take care of any updating that is necessary */
889 old_setup_level = config.c_setup_level;
891 if (old_setup_level == 0) goto NEW_INST;
893 if (old_setup_level < 323) {
894 important_message("Citadel/UX Setup",
895 "This Citadel/UX installation is too old to be upgraded.");
899 write_config_to_disk();
901 if ((config.c_setup_level / 10) == 32) {
902 important_msgnum(31);
906 if (config.c_setup_level < 400) {
907 config.c_setup_level = 400;
910 /* end of 3.23 -> 4.00 update section */
912 /* end of 4.00 -> 4.02 update section */
914 old_setup_level = config.c_setup_level;
916 /* end of version update section */
919 config.c_setup_level = REV_LEVEL;
921 /******************************************/
923 write_config_to_disk();
925 system("mkdir info 2>/dev/null"); /* Create these */
926 system("mkdir bio 2>/dev/null");
927 system("mkdir userpics 2>/dev/null");
928 system("mkdir messages 2>/dev/null");
929 system("mkdir help 2>/dev/null");
930 system("mkdir images 2>/dev/null");
931 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
934 /* Delete a bunch of old files from Citadel v4; don't need anymore */
935 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
937 check_services_entry(); /* Check /etc/services */
938 check_inittab_entry(); /* Check /etc/inittab */
940 if ((pw = getpwuid(config.c_bbsuid)) == NULL)
945 progress("Setting file permissions", 0, 3);
946 chown(".", config.c_bbsuid, gid);
947 progress("Setting file permissions", 1, 3);
948 chown("citadel.config", config.c_bbsuid, gid);
949 progress("Setting file permissions", 2, 3);
950 sprintf(aaa, "find . -exec chown %d:%d {} \\; 2>/dev/null",
951 config.c_bbsuid, gid);
953 progress("Setting file permissions", 3, 3);
955 important_message("Setup finished",
956 "Setup is finished. You may now start the Citadel server.");