]> code.citadel.org Git - citadel.git/blob - citadel/setup.c
* setup.c: allow specification of the Citadel system account by either
[citadel.git] / citadel / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel/UX setup utility
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <pwd.h>
23
24 #include "citadel.h"
25 #include "axdefs.h"
26 #include "sysdep.h"
27 #include "config.h"
28 #include "tools.h"
29
30 #ifndef DISABLE_CURSES
31 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
32 #ifdef HAVE_NCURSES_H
33 #include <ncurses.h>
34 #else
35 #include <curses.h>
36 #endif
37 #endif
38 #endif
39
40 #define MAXSETUP 3      /* How many setup questions to ask */
41
42 #define UI_TEXT         0       /* Default setup type -- text only */
43 #define UI_DIALOG       1       /* Use the 'dialog' program (REMOVED) */
44 #define UI_CURSES       2       /* Use curses */
45 #define UI_SILENT       3       /* Silent running, for use in scripts */
46
47 #define SERVICE_NAME    "citadel"
48 #define PROTO_NAME      "tcp"
49
50 int setup_type;
51 char setup_directory[SIZ];
52 char init_entry[SIZ];
53
54 char *setup_titles[] =
55 {
56         "Citadel Home Directory",
57         "System Administrator",
58         "Citadel User ID",
59         "Server port number",
60 };
61
62
63 char *setup_text[] =
64 {
65 "Enter the full pathname of the directory in which the Citadel installation\n"
66 "you are creating or updating resides.  If you specify a directory other\n"
67 "than the default, you will need to specify the -h flag to the server when\n"
68 "you start it up.\n",
69
70 "Enter the name of the system administrator (which is probably you).\n"
71 "When an account is created with this name, it will automatically be\n"
72 "assigned the highest access level.\n",
73
74 "Citadel needs to run under its own user ID.  This would typically be\n"
75 "called \"citadel\", but if you are running Citadel as a public BBS, you\n"
76 "might also call it \"bbs\" or \"guest\".  The server will run under this\n"
77 "user ID.  Please specify that user ID here.  You may specify either a\n"
78 "user name or a numeric UID.\n",
79
80 "Specify the TCP port number on which your server will run.  Normally, this\n"
81 "will be port 504, which is the official port assigned by the IANA for\n"
82 "Citadel servers.  You'll only need to specify a different port number if\n"
83 "you run multiple instances of Citadel on the same computer and there's\n"
84 "something else already using port 504.\n",
85
86 "Setup has detected that you currently have data files from a Citadel/UX\n"
87 "version 3.2x installation.  The program 'conv_32_40' can upgrade your\n"
88 "files to version 4.0x format.\n"
89 " Setup will now exit.  Please either run 'conv_32_40' or delete your data\n"
90 "files, and run setup again.\n"
91
92 };
93
94 struct config config;
95 int direction;
96
97 /*
98  * Set an entry in inittab to the desired state
99  */
100 void set_init_entry(char *which_entry, char *new_state) {
101         char *inittab = NULL;
102         FILE *fp;
103         char buf[SIZ];
104         char entry[SIZ];
105         char levels[SIZ];
106         char state[SIZ];
107         char prog[SIZ];
108
109         inittab = strdup("");
110         if (inittab == NULL) return;
111
112         fp = fopen("/etc/inittab", "r");
113         if (fp == NULL) return;
114
115         while(fgets(buf, sizeof buf, fp) != NULL) {
116
117                 if (num_tokens(buf, ':') == 4) {
118                         extract_token(entry, buf, 0, ':');
119                         extract_token(levels, buf, 1, ':');
120                         extract_token(state, buf, 2, ':');
121                         extract_token(prog, buf, 3, ':'); /* includes 0x0a LF */
122
123                         if (!strcmp(entry, which_entry)) {
124                                 strcpy(state, new_state);
125                                 sprintf(buf, "%s:%s:%s:%s",
126                                         entry, levels, state, prog);
127                         }
128                 }
129
130                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
131                 if (inittab == NULL) {
132                         fclose(fp);
133                         return;
134                 }
135                 
136                 strcat(inittab, buf);
137         }
138         fclose(fp);
139         fp = fopen("/etc/inittab", "w");
140         if (fp != NULL) {
141                 fwrite(inittab, strlen(inittab), 1, fp);
142                 fclose(fp);
143                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
144         }
145         free(inittab);
146 }
147
148
149
150
151 /* 
152  * Shut down the Citadel service if necessary, during setup.
153  */
154 void shutdown_service(void) {
155         FILE *infp;
156         char buf[SIZ];
157         char looking_for[SIZ];
158         int have_entry = 0;
159         char entry[SIZ];
160         char prog[SIZ];
161
162         strcpy(init_entry, "");
163
164         /* Determine the fully qualified path name of citserver */
165         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
166
167         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
168          * an entry is found which we believe starts citserver.
169          */
170         infp = fopen("/etc/inittab", "r");
171         if (infp == NULL) {
172                 return;
173         } else {
174                 while (fgets(buf, sizeof buf, infp) != NULL) {
175                         buf[strlen(buf) - 1] = 0;
176                         extract_token(entry, buf, 0, ':');      
177                         extract_token(prog, buf, 3, ':');
178                         if (!strncasecmp(prog, looking_for,
179                            strlen(looking_for))) {
180                                 ++have_entry;
181                                 strcpy(init_entry, entry);
182                         }
183                 }
184                 fclose(infp);
185         }
186
187         /* Bail out if there's nothing to do. */
188         if (!have_entry) return;
189
190         set_init_entry(init_entry, "off");
191 }
192
193
194 /*
195  * Start the Citadel service.
196  */
197 void start_the_service(void) {
198         if (strlen(init_entry) > 0) {
199                 set_init_entry(init_entry, "respawn");
200         }
201 }
202
203
204
205 void cleanup(int exitcode)
206 {
207 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
208         if (setup_type == UI_CURSES) {
209                 clear();
210                 refresh();
211                 endwin();
212         }
213 #endif
214
215         exit(exitcode);
216 }
217
218
219 /* Gets a line from the terminal */
220 /* Where on the screen to start */
221 /* Pointer to string buffer */
222 /* Maximum length - if negative, no-show */
223 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
224 void getlin(int yp, int xp, char *string, int lim) {
225         int a, b;
226         char flag;
227
228         flag = 0;
229         if (lim < 0) {
230                 lim = (0 - lim);
231                 flag = 1;
232         }
233         move(yp, xp);
234         standout();
235         for (a = 0; a < lim; ++a)
236                 addch('-');
237         refresh();
238         move(yp, xp);
239         for (a = 0; a < lim; ++a)
240                 addch(' ');
241         move(yp, xp);
242         printw("%s", string);
243       GLA:move(yp, xp + strlen(string));
244         refresh();
245         a = getch();
246         if (a == 127)
247                 a = 8;
248         a = (a & 127);
249         if (a == 10)
250                 a = 13;
251         if ((a == 8) && (strlen(string) == 0))
252                 goto GLA;
253         if ((a != 13) && (a != 8) && (strlen(string) == lim))
254                 goto GLA;
255         if ((a == 8) && (string[0] != 0)) {
256                 string[strlen(string) - 1] = 0;
257                 move(yp, xp + strlen(string));
258                 addch(' ');
259                 goto GLA;
260         }
261         if ((a == 13) || (a == 10)) {
262                 standend();
263                 move(yp, xp);
264                 for (a = 0; a < lim; ++a)
265                         addch(' ');
266                 mvprintw(yp, xp, "%s", string);
267                 refresh();
268                 return;
269         }
270         b = strlen(string);
271         string[b] = a;
272         string[b + 1] = 0;
273         if (flag == 0)
274                 addch(a);
275         if (flag == 1)
276                 addch('*');
277         goto GLA;
278 }
279 #endif
280
281
282
283 void title(char *text)
284 {
285         if (setup_type == UI_TEXT) {
286                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
287         }
288 }
289
290
291 void hit_any_key(void)
292 {
293         char junk[5];
294
295 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
296         if (setup_type == UI_CURSES) {
297                 mvprintw(20, 0, "Press any key to continue... ");
298                 refresh();
299                 getch();
300                 return;
301         }
302 #endif
303         if (setup_type == UI_TEXT) {
304                 printf("Press return to continue...");
305                 fgets(junk, 5, stdin);
306         }
307 }
308
309 int yesno(char *question)
310 {
311         int answer = 0;
312         char buf[4096];
313
314         switch (setup_type) {
315
316         case UI_TEXT:
317                 do {
318                         printf("%s\nYes/No --> ", question);
319                         fgets(buf, 4096, stdin);
320                         answer = tolower(buf[0]);
321                         if (answer == 'y')
322                                 answer = 1;
323                         else if (answer == 'n')
324                                 answer = 0;
325                 } while ((answer < 0) || (answer > 1));
326                 break;
327
328 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
329         case UI_CURSES:
330                 do {
331                         clear();
332                         standout();
333                         mvprintw(1, 20, "Question");
334                         standend();
335                         mvprintw(10, 0, "%-80s", question);
336                         mvprintw(20, 0, "%80s", "");
337                         mvprintw(20, 0, "Yes/No -> ");
338                         refresh();
339                         answer = getch();
340                         answer = tolower(answer);
341                         if (answer == 'y')
342                                 answer = 1;
343                         else if (answer == 'n')
344                                 answer = 0;
345                 } while ((answer < 0) || (answer > 1));
346                 break;
347 #endif
348
349         }
350         return (answer);
351 }
352
353
354 void important_message(char *title, char *msgtext)
355 {
356
357         switch (setup_type) {
358
359         case UI_TEXT:
360                 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");
361                 printf("       %s \n\n%s\n\n", title, msgtext);
362                 hit_any_key();
363                 break;
364
365 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
366         case UI_CURSES:
367                 clear();
368                 move(1, 20);
369                 standout();
370                 printw("  Important Message  ");
371                 standend();
372                 move(3, 0);
373                 printw("%s", msgtext);
374                 refresh();
375                 hit_any_key();
376                 break;
377 #endif
378
379         }
380 }
381
382 void important_msgnum(int msgnum)
383 {
384         important_message("Important Message", setup_text[msgnum]);
385 }
386
387 void display_error(char *error_message)
388 {
389         important_message("Error", error_message);
390 }
391
392 void progress(char *text, long int curr, long int cmax)
393 {
394         static long dots_printed;
395         long a;
396
397         switch (setup_type) {
398
399         case UI_TEXT:
400                 if (curr == 0) {
401                         printf("%s\n", text);
402                         printf("..........................");
403                         printf("..........................");
404                         printf("..........................\r");
405                         fflush(stdout);
406                         dots_printed = 0;
407                 } else if (curr == cmax) {
408                         printf("\r%79s\n", "");
409                 } else {
410                         a = (curr * 100) / cmax;
411                         a = a * 78;
412                         a = a / 100;
413                         while (dots_printed < a) {
414                                 printf("*");
415                                 ++dots_printed;
416                                 fflush(stdout);
417                         }
418                 }
419                 break;
420
421 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
422         case UI_CURSES:
423                 if (curr == 0) {
424                         clear();
425                         move(5, 20);
426                         printw("%s\n", text);
427                         move(10, 1);
428                         printf("..........................");
429                         printf("..........................");
430                         printf("..........................\r");
431                         refresh();
432                         dots_printed = 0;
433                 } else if (curr == cmax) {
434                         clear();
435                         refresh();
436                 } else {
437                         a = (curr * 100) / cmax;
438                         a = a * 78;
439                         a = a / 100;
440                         move(10, 1);
441                         dots_printed = 0;
442                         while (dots_printed < a) {
443                                 printw("*");
444                                 ++dots_printed;
445                         }
446                         refresh();
447                 }
448                 break;
449 #endif
450
451         }
452 }
453
454
455
456 /*
457  * check_services_entry()  -- Make sure "citadel" is in /etc/services
458  *
459  */
460 void check_services_entry(void)
461 {
462         int i;
463         FILE *sfp;
464
465         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
466                 for (i=0; i<3; ++i) {
467                         progress("Adding service entry...", i, 3);
468                         if (i == 0) {
469                                 sfp = fopen("/etc/services", "a");
470                                 if (sfp == NULL) {
471                                         display_error(strerror(errno));
472                                 } else {
473                                         fprintf(sfp, "%s                504/tcp\n",
474                                                 SERVICE_NAME);
475                                         fclose(sfp);
476                                 }
477                         }
478                         sleep(1);
479                 }
480         }
481 }
482
483
484 /*
485  * check_inittab_entry()  -- Make sure "citadel" is in /etc/inittab
486  *
487  */
488 void check_inittab_entry(void)
489 {
490         FILE *infp;
491         char buf[SIZ];
492         char looking_for[SIZ];
493         char question[128];
494         char entryname[5];
495
496         /* Determine the fully qualified path name of citserver */
497         snprintf(looking_for, sizeof looking_for, "%s/citserver ", BBSDIR);
498
499         /* If there's already an entry, then we have nothing left to do. */
500         if (strlen(init_entry) > 0) {
501                 return;
502         }
503
504         /* Otherwise, prompt the user to create an entry. */
505         snprintf(question, sizeof question,
506                 "There is no '%s' entry in /etc/inittab.\n"
507                 "Would you like to add one?",
508                 looking_for);
509         if (yesno(question) == 0)
510                 return;
511
512         /* Generate a unique entry name for /etc/inittab */
513         snprintf(entryname, sizeof entryname, "c0");
514         do {
515                 ++entryname[1];
516                 if (entryname[1] > '9') {
517                         entryname[1] = 0;
518                         ++entryname[0];
519                         if (entryname[0] > 'z') {
520                                 display_error(
521                                    "Can't generate a unique entry name");
522                                 return;
523                         }
524                 }
525                 snprintf(buf, sizeof buf,
526                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
527         } while (system(buf) == 0);
528
529         /* Now write it out to /etc/inittab */
530         infp = fopen("/etc/inittab", "a");
531         if (infp == NULL) {
532                 display_error(strerror(errno));
533         } else {
534                 fprintf(infp, "# Start the Citadel/UX server...\n");
535                 fprintf(infp, "%s:2345:respawn:%s -h%s\n",
536                         entryname, looking_for, setup_directory);
537                 fclose(infp);
538                 strcpy(init_entry, entryname);
539         }
540 }
541
542
543
544 void set_str_val(int msgpos, char str[])
545 {
546         char buf[4096];
547         char tempfile[PATH_MAX];
548         char setupmsg[SIZ];
549
550         strcpy(tempfile, tmpnam(NULL));
551         strcpy(setupmsg, "");
552
553         switch (setup_type) {
554         case UI_TEXT:
555                 title(setup_titles[msgpos]);
556                 printf("\n%s\n", setup_text[msgpos]);
557                 printf("This is currently set to:\n%s\n", str);
558                 printf("Enter new value or press return to leave unchanged:\n");
559                 fgets(buf, 4096, stdin);
560                 buf[strlen(buf) - 1] = 0;
561                 if (strlen(buf) != 0)
562                         strcpy(str, buf);
563                 break;
564 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
565         case UI_CURSES:
566                 clear();
567                 move(1, ((80 - strlen(setup_titles[msgpos])) / 2));
568                 standout();
569                 printw("%s", setup_titles[msgpos]);
570                 standend();
571                 move(3, 0);
572                 printw("%s", setup_text[msgpos]);
573                 refresh();
574                 getlin(20, 0, str, 80);
575                 break;
576 #endif
577         }
578 }
579
580 void set_int_val(int msgpos, int *ip)
581 {
582         char buf[16];
583         snprintf(buf, sizeof buf, "%d", (int) *ip);
584         set_str_val(msgpos, buf);
585         *ip = atoi(buf);
586 }
587
588
589 void set_char_val(int msgpos, char *ip)
590 {
591         char buf[16];
592         snprintf(buf, sizeof buf, "%d", (int) *ip);
593         set_str_val(msgpos, buf);
594         *ip = (char) atoi(buf);
595 }
596
597
598 void set_long_val(int msgpos, long int *ip)
599 {
600         char buf[16];
601         snprintf(buf, sizeof buf, "%ld", *ip);
602         set_str_val(msgpos, buf);
603         *ip = atol(buf);
604 }
605
606
607 void edit_value(int curr)
608 {
609         int i;
610         struct passwd *pw;
611         char bbsuidname[SIZ];
612
613         switch (curr) {
614
615         case 1:
616                 set_str_val(curr, config.c_sysadm);
617                 break;
618
619         case 2:
620                 i = config.c_bbsuid;
621                 pw = getpwuid(i);
622                 if (pw == NULL) {
623                         set_int_val(curr, &i);
624                         config.c_bbsuid = i;
625                 }
626                 else {
627                         strcpy(bbsuidname, pw->pw_name);
628                         set_str_val(curr, bbsuidname);
629                         pw = getpwnam(bbsuidname);
630                         if (pw != NULL) {
631                                 config.c_bbsuid = pw->pw_uid;
632                         }
633                         else if (atoi(bbsuidname) > 0) {
634                                 config.c_bbsuid = atoi(bbsuidname);
635                         }
636                 }
637                 break;
638
639         case 3:
640                 set_int_val(curr, &config.c_port_number);
641                 break;
642
643
644         }
645 }
646
647 /*
648  * (re-)write the config data to disk
649  */
650 void write_config_to_disk(void)
651 {
652         FILE *fp;
653         int fd;
654
655         if ((fd = creat("citadel.config", S_IRUSR | S_IWUSR)) == -1) {
656                 display_error("setup: cannot open citadel.config");
657                 cleanup(1);
658         }
659         fp = fdopen(fd, "wb");
660         if (fp == NULL) {
661                 display_error("setup: cannot open citadel.config");
662                 cleanup(1);
663         }
664         fwrite((char *) &config, sizeof(struct config), 1, fp);
665         fclose(fp);
666 }
667
668
669
670
671 /*
672  * Figure out what type of user interface we're going to use
673  */
674 int discover_ui(void)
675 {
676
677 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
678         return UI_CURSES;
679 #endif
680         return UI_TEXT;
681 }
682
683
684
685
686
687 int main(int argc, char *argv[])
688 {
689         int a;
690         int curr;
691         char aaa[128];
692         FILE *fp;
693         int old_setup_level = 0;
694         int info_only = 0;
695         struct utsname my_utsname;
696         struct passwd *pw;
697         struct hostent *he;
698         gid_t gid;
699
700         /* set an invalid setup type */
701         setup_type = (-1);
702
703         /* parse command line args */
704         for (a = 0; a < argc; ++a) {
705                 if (!strncmp(argv[a], "-u", 2)) {
706                         strcpy(aaa, argv[a]);
707                         strcpy(aaa, &aaa[2]);
708                         setup_type = atoi(aaa);
709                 }
710                 if (!strcmp(argv[a], "-i")) {
711                         info_only = 1;
712                 }
713                 if (!strcmp(argv[a], "-q")) {
714                         setup_type = UI_SILENT;
715                 }
716         }
717
718
719         /* If a setup type was not specified, try to determine automatically
720          * the best one to use out of all available types.
721          */
722         if (setup_type < 0) {
723                 setup_type = discover_ui();
724         }
725 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
726         if (setup_type == UI_CURSES) {
727                 initscr();
728                 raw();
729                 noecho();
730         }
731 #endif
732
733         if (info_only == 1) {
734                 important_message("Citadel/UX Setup", CITADEL);
735                 cleanup(0);
736         }
737
738         /* Get started in a valid setup directory. */
739         strcpy(setup_directory, BBSDIR);
740         set_str_val(0, setup_directory);
741         if (chdir(setup_directory) != 0) {
742                 important_message("Citadel/UX Setup",
743                           "The directory you specified does not exist.");
744                 cleanup(errno);
745         }
746
747         /* Determine our host name, in case we need to use it as a default */
748         uname(&my_utsname);
749
750         /* See if we need to shut down the Citadel service. */
751         for (a=0; a<=5; ++a) {
752                 progress("Shutting down the Citadel service...", a, 5);
753                 if (a == 0) shutdown_service();
754                 sleep(1);
755         }
756
757         /* Now begin. */
758         switch (setup_type) {
759
760         case UI_TEXT:
761                 printf("\n\n\n"
762                         "               *** Citadel/UX setup program ***\n\n");
763                 break;
764
765         }
766
767         /*
768          * What we're going to try to do here is append a whole bunch of
769          * nulls to the citadel.config file, so we can keep the old config
770          * values if they exist, but if the file is missing or from an
771          * earlier version with a shorter config structure, when setup tries
772          * to read the old config parameters, they'll all come up zero.
773          * The length of the config file will be set to what it's supposed
774          * to be when we rewrite it, because we replace the old file with a
775          * completely new copy.
776          */
777
778         if ((a = open("citadel.config", O_WRONLY | O_CREAT | O_APPEND,
779                       S_IRUSR | S_IWUSR)) == -1) {
780                 display_error("setup: cannot append citadel.config");
781                 cleanup(errno);
782         }
783         fp = fdopen(a, "ab");
784         if (fp == NULL) {
785                 display_error("setup: cannot append citadel.config");
786                 cleanup(errno);
787         }
788         for (a = 0; a < sizeof(struct config); ++a)
789                 putc(0, fp);
790         fclose(fp);
791
792         /* now we re-open it, and read the old or blank configuration */
793         fp = fopen("citadel.config", "rb");
794         if (fp == NULL) {
795                 display_error("setup: cannot open citadel.config");
796                 cleanup(errno);
797         }
798         fread((char *) &config, sizeof(struct config), 1, fp);
799         fclose(fp);
800
801         /* set some sample/default values in place of blanks... */
802         if (strlen(config.c_nodename) == 0)
803                 safestrncpy(config.c_nodename, my_utsname.nodename,
804                             sizeof config.c_nodename);
805         strtok(config.c_nodename, ".");
806         if (strlen(config.c_fqdn) == 0) {
807                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
808                         safestrncpy(config.c_fqdn, he->h_name,
809                                     sizeof config.c_fqdn);
810                 else
811                         safestrncpy(config.c_fqdn, my_utsname.nodename,
812                                     sizeof config.c_fqdn);
813         }
814         if (strlen(config.c_humannode) == 0)
815                 strcpy(config.c_humannode, "My System");
816         if (strlen(config.c_phonenum) == 0)
817                 strcpy(config.c_phonenum, "US 800 555 1212");
818         if (config.c_initax == 0) {
819                 config.c_initax = 4;
820         }
821         if (strlen(config.c_moreprompt) == 0)
822                 strcpy(config.c_moreprompt, "<more>");
823         if (strlen(config.c_twitroom) == 0)
824                 strcpy(config.c_twitroom, "Trashcan");
825         if (strlen(config.c_bucket_dir) == 0)
826                 strcpy(config.c_bucket_dir, "bitbucket");
827         if (strlen(config.c_net_password) == 0)
828                 strcpy(config.c_net_password, "netpassword");
829         if (strlen(config.c_baseroom) == 0)
830                 strcpy(config.c_baseroom, "Lobby");
831         if (strlen(config.c_aideroom) == 0)
832                 strcpy(config.c_aideroom, "Aide");
833         if (config.c_port_number == 0) {
834                 config.c_port_number = 504;
835         }
836         if (config.c_ipgm_secret == 0) {
837                 srand(getpid());
838                 config.c_ipgm_secret = rand();
839         }
840         if (config.c_sleeping == 0) {
841                 config.c_sleeping = 900;
842         }
843         if (config.c_bbsuid == 0) {
844                 pw = getpwnam("citadel");
845                 if (pw != NULL)
846                         config.c_bbsuid = pw->pw_uid;
847         }
848         if (config.c_bbsuid == 0) {
849                 pw = getpwnam("bbs");
850                 if (pw != NULL)
851                         config.c_bbsuid = pw->pw_uid;
852         }
853         if (config.c_bbsuid == 0) {
854                 pw = getpwnam("guest");
855                 if (pw != NULL)
856                         config.c_bbsuid = pw->pw_uid;
857         }
858         if (config.c_createax == 0) {
859                 config.c_createax = 3;
860         }
861         /*
862          * Negative values for maxsessions are not allowed.
863          */
864         if (config.c_maxsessions < 0) {
865                 config.c_maxsessions = 0;
866         }
867         /* We need a system default message expiry policy, because this is
868          * the top level and there's no 'higher' policy to fall back on.
869          */
870         if (config.c_ep.expire_mode == 0) {
871                 config.c_ep.expire_mode = EXPIRE_NUMMSGS;
872                 config.c_ep.expire_value = 150;
873         }
874
875         /*
876          * Default port numbers for various services
877          */
878         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
879         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
880         if (config.c_imap_port == 0) config.c_imap_port = 143;
881
882         /* Go through a series of dialogs prompting for config info */
883         if (setup_type != UI_SILENT) {
884                 for (curr = 1; curr <= MAXSETUP; ++curr) {
885                         edit_value(curr);
886                 }
887         }
888
889         /*
890            if (setuid(config.c_bbsuid) != 0) {
891            important_message("Citadel/UX Setup",
892            "Failed to change the user ID to your Citadel user.");
893            cleanup(errno);
894            }
895          */
896
897 /***** begin version update section ***** */
898         /* take care of any updating that is necessary */
899
900         old_setup_level = config.c_setup_level;
901
902         if (old_setup_level == 0)
903                 goto NEW_INST;
904
905         if (old_setup_level < 323) {
906                 important_message("Citadel/UX Setup",
907                                   "This Citadel/UX installation is too old "
908                                   "to be upgraded.");
909                 cleanup(1);
910         }
911         write_config_to_disk();
912
913         if ((config.c_setup_level / 10) == 32) {
914                 important_msgnum(31);
915                 cleanup(0);
916         }
917         if (config.c_setup_level < 400) {
918                 config.c_setup_level = 400;
919         }
920         /* end of 3.23 -> 4.00 update section */
921
922         /* end of 4.00 -> 4.02 update section */
923
924         old_setup_level = config.c_setup_level;
925
926         /* end of version update section */
927
928 NEW_INST:
929         config.c_setup_level = REV_LEVEL;
930
931 /******************************************/
932
933         write_config_to_disk();
934
935         mkdir("info", 0700);
936         mkdir("bio", 0700);
937         mkdir("userpics", 0700);
938         mkdir("messages", 0700);
939         mkdir("help", 0700);
940         mkdir("images", 0700);
941         mkdir("netconfigs", 0700);
942         mkdir(config.c_bucket_dir, 0700);
943
944         /* Delete a bunch of old files from Citadel v4; don't need anymore */
945         system("rm -fr ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
946
947         check_services_entry(); /* Check /etc/services */
948         check_inittab_entry();  /* Check /etc/inittab */
949
950         if ((pw = getpwuid(config.c_bbsuid)) == NULL)
951                 gid = getgid();
952         else
953                 gid = pw->pw_gid;
954
955         progress("Setting file permissions", 0, 5);
956         chown(".", config.c_bbsuid, gid);
957         progress("Setting file permissions", 1, 5);
958         chown("citadel.config", config.c_bbsuid, gid);
959         progress("Setting file permissions", 2, 5);
960         snprintf(aaa, sizeof aaa,
961                 "find . | grep -v chkpwd | xargs chown %ld:%ld 2>/dev/null",
962                 (long)config.c_bbsuid, (long)gid);
963         system(aaa);
964         progress("Setting file permissions", 3, 5);
965         chmod("citadel.config", S_IRUSR | S_IWUSR);
966         progress("Setting file permissions", 4, 5);
967
968         /* See if we can start the Citadel service. */
969         if (strlen(init_entry) > 0) {
970                 for (a=0; a<=5; ++a) {
971                         progress("Starting the Citadel service...", a, 5);
972                         if (a == 0) start_the_service();
973                         sleep(1);
974                 }
975                 important_message("Setup finished",
976                         "Setup is finished.  You may now log in.");
977         }
978         else {
979                 important_message("Setup finished",
980                         "Setup is finished.  You may now start the server.");
981         }
982
983         cleanup(0);
984         return 0;
985 }