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");
486 display_error(strerror(errno));
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.%d", 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;
711 /* set an invalid setup type */
714 /* parse command line args */
715 for (a=0; a<argc; ++a) {
716 if (!strncmp(argv[a], "-u", 2)) {
717 strcpy(aaa, argv[a]);
718 strcpy(aaa, &aaa[2]);
719 setup_type = atoi(aaa);
721 if (!strcmp(argv[a], "-i")) {
727 /* If a setup type was not specified, try to determine automatically
728 * the best one to use out of all available types.
730 if (setup_type < 0) {
731 setup_type = discover_ui();
735 if (setup_type == UI_CURSES) {
742 if (info_only == 1) {
743 important_message("Citadel/UX Setup", CITADEL);
747 /* Get started in a valid setup directory. */
748 strcpy(setup_directory, BBSDIR);
749 set_str_val(0, setup_directory);
750 if (chdir(setup_directory) != 0) {
751 important_message("Citadel/UX Setup",
752 "The directory you specified does not exist.");
756 /* Determine our host name, in case we need to use it as a default */
763 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
767 system("exec clear");
773 * What we're going to try to do here is append a whole bunch of
774 * nulls to the citadel.config file, so we can keep the old config
775 * values if they exist, but if the file is missing or from an
776 * earlier version with a shorter config structure, when setup tries
777 * to read the old config parameters, they'll all come up zero.
778 * The length of the config file will be set to what it's supposed
779 * to be when we rewrite it, because we replace the old file with a
780 * completely new copy. (Neat, eh?)
783 if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
784 S_IRUSR | S_IWUSR)) == -1) {
785 display_error("setup: cannot append citadel.config");
791 display_error("setup: cannot append citadel.config");
794 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
797 /* now we re-open it, and read the old or blank configuration */
798 fp=fopen("citadel.config","rb");
800 display_error("setup: cannot open citadel.config");
803 fread((char *)&config,sizeof(struct config),1,fp);
807 /* set some sample/default values in place of blanks... */
808 if (strlen(config.c_nodename)==0)
809 safestrncpy(config.c_nodename, my_utsname.nodename,
810 sizeof config.c_nodename);
811 strtok(config.c_nodename, ".");
812 if (strlen(config.c_fqdn)==0) {
813 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
814 safestrncpy(config.c_fqdn, he->h_name,
815 sizeof config.c_fqdn);
817 safestrncpy(config.c_fqdn, my_utsname.nodename,
818 sizeof config.c_fqdn);
820 if (strlen(config.c_humannode)==0)
821 strcpy(config.c_humannode,"My System");
822 if (strlen(config.c_phonenum)==0)
823 strcpy(config.c_phonenum,"US 800 555 1212");
824 if (config.c_initax == 0)
826 if (strlen(config.c_moreprompt)==0)
827 strcpy(config.c_moreprompt,"<more>");
828 if (strlen(config.c_twitroom)==0)
829 strcpy(config.c_twitroom,"Trashcan");
830 if (strlen(config.c_bucket_dir)==0)
831 strcpy(config.c_bucket_dir,"bitbucket");
832 if (strlen(config.c_net_password)==0)
833 strcpy(config.c_net_password,"netpassword");
834 if (config.c_port_number == 0) {
835 config.c_port_number = 504;
837 if (config.c_ipgm_secret == 0) {
839 config.c_ipgm_secret = rand();
841 if (config.c_sleeping == 0) {
842 config.c_sleeping = 900;
844 if (config.c_bbsuid == 0) {
845 pw = getpwnam("citadel");
846 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
848 if (config.c_bbsuid == 0) {
849 pw = getpwnam("bbs");
850 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
852 if (config.c_bbsuid == 0) {
853 pw = getpwnam("guest");
854 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
858 * Negative values for maxsessions are not allowed.
860 if (config.c_maxsessions < 0) {
861 config.c_maxsessions = 0;
864 /* We need a system default message expiry policy, because this is
865 * the top level and there's no 'higher' policy to fall back on.
867 if (config.c_ep.expire_mode == 0) {
868 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
869 config.c_ep.expire_value = 150;
872 /* Go through a series of dialogs prompting for config info */
873 for (curr = 1; curr <= MAXSETUP; ++curr) {
878 if (setuid(config.c_bbsuid) != 0) {
879 important_message("Citadel/UX Setup",
880 "Failed to change the user ID to your BBS user.");
885 /***** begin version update section ***** */
886 /* take care of any updating that is necessary */
888 old_setup_level = config.c_setup_level;
890 if (old_setup_level == 0) goto NEW_INST;
892 if (old_setup_level < 323) {
893 important_message("Citadel/UX Setup",
894 "This Citadel/UX installation is too old to be upgraded.");
898 write_config_to_disk();
900 if ((config.c_setup_level / 10) == 32) {
901 important_msgnum(31);
905 if (config.c_setup_level < 400) {
906 config.c_setup_level = 400;
909 /* end of 3.23 -> 4.00 update section */
911 /* end of 4.00 -> 4.02 update section */
913 old_setup_level = config.c_setup_level;
915 /* end of version update section */
918 config.c_setup_level = REV_LEVEL;
920 /******************************************/
922 write_config_to_disk();
924 system("mkdir info 2>/dev/null"); /* Create these */
925 system("mkdir bio 2>/dev/null");
926 system("mkdir userpics 2>/dev/null");
927 system("mkdir messages 2>/dev/null");
928 system("mkdir help 2>/dev/null");
929 system("mkdir images 2>/dev/null");
930 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
933 /* Delete a bunch of old files from Citadel v4; don't need anymore */
934 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
936 check_services_entry(); /* Check /etc/services */
937 check_inittab_entry(); /* Check /etc/inittab */
939 progress("Setting file permissions", 0, 3);
940 chown(".", config.c_bbsuid, getgid());
941 progress("Setting file permissions", 1, 3);
942 chown("citadel.config", config.c_bbsuid, getgid());
943 progress("Setting file permissions", 2, 3);
944 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
947 progress("Setting file permissions", 3, 3);
949 important_message("Setup finished",
950 "Setup is finished. You may now start the Citadel server.");