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) {
661 fp=fopen("citadel.config","wb");
663 display_error("setup: cannot open citadel.config");
666 fwrite((char *)&config,sizeof(struct config),1,fp);
674 * Figure out what type of user interface we're going to use
676 int discover_ui(void) {
682 if (system("dialog -h </dev/null 2>&1 |grep Savio")==0) {
693 int main(int argc, char *argv[]) {
698 int old_setup_level = 0;
700 struct utsname my_utsname;
703 /* set an invalid setup type */
706 /* parse command line args */
707 for (a=0; a<argc; ++a) {
708 if (!strncmp(argv[a], "-u", 2)) {
709 strcpy(aaa, argv[a]);
710 strcpy(aaa, &aaa[2]);
711 setup_type = atoi(aaa);
713 if (!strcmp(argv[a], "-i")) {
719 /* If a setup type was not specified, try to determine automatically
720 * the best one to use out of all available types.
722 if (setup_type < 0) {
723 setup_type = discover_ui();
727 if (setup_type == UI_CURSES) {
734 if (info_only == 1) {
735 important_message("Citadel/UX Setup", CITADEL);
739 /* Get started in a valid setup directory. */
740 strcpy(setup_directory, BBSDIR);
741 set_str_val(0, setup_directory);
742 if (chdir(setup_directory) != 0) {
743 important_message("Citadel/UX Setup",
744 "The directory you specified does not exist.");
748 /* Determine our host name, in case we need to use it as a default */
755 printf("\n\n\n *** Citadel/UX setup program ***\n\n");
759 system("exec clear");
765 * What we're going to try to do here is append a whole bunch of
766 * nulls to the citadel.config file, so we can keep the old config
767 * values if they exist, but if the file is missing or from an
768 * earlier version with a shorter config structure, when setup tries
769 * to read the old config parameters, they'll all come up zero.
770 * The length of the config file will be set to what it's supposed
771 * to be when we rewrite it, because we replace the old file with a
772 * completely new copy. (Neat, eh?)
775 fp=fopen("citadel.config","ab");
777 display_error("setup: cannot append citadel.config");
780 for (a=0; a<sizeof(struct config); ++a) putc(0,fp);
783 /* now we re-open it, and read the old or blank configuration */
784 fp=fopen("citadel.config","rb");
786 display_error("setup: cannot open citadel.config");
789 fread((char *)&config,sizeof(struct config),1,fp);
793 /* set some sample/default values in place of blanks... */
794 if (strlen(config.c_nodename)==0)
795 strcpy(config.c_nodename, my_utsname.nodename);
796 if (strlen(config.c_fqdn)==0)
797 sprintf(config.c_fqdn, "%s.local", my_utsname.nodename);
798 if (strlen(config.c_humannode)==0)
799 strcpy(config.c_humannode,"My System");
800 if (strlen(config.c_phonenum)==0)
801 strcpy(config.c_phonenum,"US 800 555 1212");
802 if (config.c_initax == 0)
804 if (strlen(config.c_moreprompt)==0)
805 strcpy(config.c_moreprompt,"<more>");
806 if (strlen(config.c_twitroom)==0)
807 strcpy(config.c_twitroom,"Trashcan");
808 if (strlen(config.c_bucket_dir)==0)
809 strcpy(config.c_bucket_dir,"bitbucket");
810 if (strlen(config.c_net_password)==0)
811 strcpy(config.c_net_password,"netpassword");
812 if (config.c_port_number == 0) {
813 config.c_port_number = 504;
815 if (config.c_ipgm_secret == 0) {
817 config.c_ipgm_secret = rand();
819 if (config.c_sleeping == 0) {
820 config.c_sleeping = 900;
822 if (config.c_bbsuid == 0) {
823 pw = getpwnam("citadel");
824 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
826 if (config.c_bbsuid == 0) {
827 pw = getpwnam("bbs");
828 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
830 if (config.c_bbsuid == 0) {
831 pw = getpwnam("guest");
832 if (pw != NULL) config.c_bbsuid = pw->pw_uid;
836 * Make sure that at least one concurrent session is allowed!
838 if (config.c_maxsessions < 1) {
839 config.c_maxsessions = 1;
842 /* We need a system default message expiry policy, because this is
843 * the top level and there's no 'higher' policy to fall back on.
845 if (config.c_ep.expire_mode == 0) {
846 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
847 config.c_ep.expire_value = 150;
850 /* Go through a series of dialogs prompting for config info */
851 for (curr = 1; curr <= MAXSETUP; ++curr) {
856 if (setuid(config.c_bbsuid) != 0) {
857 important_message("Citadel/UX Setup",
858 "Failed to change the user ID to your BBS user.");
863 /***** begin version update section ***** */
864 /* take care of any updating that is necessary */
866 old_setup_level = config.c_setup_level;
868 if (old_setup_level == 0) goto NEW_INST;
870 if (old_setup_level < 323) {
871 important_message("Citadel/UX Setup",
872 "This Citadel/UX installation is too old to be upgraded.");
876 write_config_to_disk();
878 if ((config.c_setup_level / 10) == 32) {
879 important_msgnum(31);
883 if (config.c_setup_level < 400) {
884 config.c_setup_level = 400;
887 /* end of 3.23 -> 4.00 update section */
889 /* end of 4.00 -> 4.02 update section */
891 old_setup_level = config.c_setup_level;
893 /* end of version update section */
896 config.c_setup_level = REV_LEVEL;
898 /******************************************/
900 write_config_to_disk();
902 system("mkdir info 2>/dev/null"); /* Create these */
903 system("mkdir bio 2>/dev/null");
904 system("mkdir userpics 2>/dev/null");
905 system("mkdir messages 2>/dev/null");
906 system("mkdir help 2>/dev/null");
907 system("mkdir images 2>/dev/null");
908 sprintf(aaa,"mkdir %s 2>/dev/null",config.c_bucket_dir);
911 /* Delete a bunch of old files from Citadel v4; don't need anymore */
912 system("rm -fr ./chatpipes ./expressmsgs sessions 2>/dev/null");
914 check_services_entry(); /* Check /etc/services */
915 check_inittab_entry(); /* Check /etc/inittab */
917 progress("Setting file permissions", 0, 3);
918 chown(".", config.c_bbsuid, getgid());
919 progress("Setting file permissions", 1, 3);
920 chown("citadel.config", config.c_bbsuid, getgid());
921 progress("Setting file permissions", 2, 3);
922 sprintf(aaa, "find . -exec chown %d {} \\; 2>/dev/null",
925 progress("Setting file permissions", 3, 3);
927 important_message("Setup finished",
928 "Setup is finished. You may now start the Citadel server.");