* Cleaned up a couple of FIXME's sitting around in the code
[citadel.git] / webcit / setup.c
1 /*
2  * $Id$
3  *
4  * WebCit setup utility
5  * 
6  * (This is basically just an install wizard.  It's not required.)
7  *
8  */
9
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <fcntl.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/utsname.h>
19 #include <sys/wait.h>
20 #include <signal.h>
21 #include <netdb.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <pwd.h>
25
26 #include "webcit.h"
27
28 #ifdef HAVE_NEWT
29 #include <newt.h>
30 #endif
31
32
33 #define UI_TEXT         0       /* Default setup type -- text only */
34 #define UI_SILENT       3       /* Silent running, for use in scripts */
35 #define UI_NEWT         4       /* Use the "newt" window library */
36
37 int setup_type;
38 char setup_directory[SIZ];
39 char init_entry[SIZ];
40 int using_web_installer = 0;
41
42
43 /*
44  * Set an entry in inittab to the desired state
45  */
46 void set_init_entry(char *which_entry, char *new_state) {
47         char *inittab = NULL;
48         FILE *fp;
49         char buf[SIZ];
50         char entry[SIZ];
51         char levels[SIZ];
52         char state[SIZ];
53         char prog[SIZ];
54
55         inittab = strdup("");
56         if (inittab == NULL) return;
57
58         fp = fopen("/etc/inittab", "r");
59         if (fp == NULL) return;
60
61         while(fgets(buf, sizeof buf, fp) != NULL) {
62
63                 if (num_tokens(buf, ':') == 4) {
64                         extract_token(entry, buf, 0, ':');
65                         extract_token(levels, buf, 1, ':');
66                         extract_token(state, buf, 2, ':');
67                         extract_token(prog, buf, 3, ':'); /* includes 0x0a LF */
68
69                         if (!strcmp(entry, which_entry)) {
70                                 strcpy(state, new_state);
71                                 sprintf(buf, "%s:%s:%s:%s",
72                                         entry, levels, state, prog);
73                         }
74                 }
75
76                 inittab = realloc(inittab, strlen(inittab) + strlen(buf) + 2);
77                 if (inittab == NULL) {
78                         fclose(fp);
79                         return;
80                 }
81                 
82                 strcat(inittab, buf);
83         }
84         fclose(fp);
85         fp = fopen("/etc/inittab", "w");
86         if (fp != NULL) {
87                 fwrite(inittab, strlen(inittab), 1, fp);
88                 fclose(fp);
89                 kill(1, SIGHUP);        /* Tell init to re-read /etc/inittab */
90         }
91         free(inittab);
92 }
93
94
95
96
97 /* 
98  * Shut down the Citadel service if necessary, during setup.
99  */
100 void shutdown_service(void) {
101         FILE *infp;
102         char buf[SIZ];
103         char looking_for[SIZ];
104         int have_entry = 0;
105         char entry[SIZ];
106         char prog[SIZ];
107
108         strcpy(init_entry, "");
109
110         /* Determine the fully qualified path name of webserver */
111         snprintf(looking_for, sizeof looking_for, "%s/webserver ", WEBCITDIR);
112
113         /* Pound through /etc/inittab line by line.  Set have_entry to 1 if
114          * an entry is found which we believe starts webserver.
115          */
116         infp = fopen("/etc/inittab", "r");
117         if (infp == NULL) {
118                 return;
119         } else {
120                 while (fgets(buf, sizeof buf, infp) != NULL) {
121                         buf[strlen(buf) - 1] = 0;
122                         extract_token(entry, buf, 0, ':');      
123                         extract_token(prog, buf, 3, ':');
124                         if (!strncasecmp(prog, looking_for,
125                            strlen(looking_for))) {
126                                 ++have_entry;
127                                 strcpy(init_entry, entry);
128                         }
129                 }
130                 fclose(infp);
131         }
132
133         /* Bail out if there's nothing to do. */
134         if (!have_entry) return;
135
136         set_init_entry(init_entry, "off");
137 }
138
139
140 /*
141  * Start the Citadel service.
142  */
143 void start_the_service(void) {
144         if (strlen(init_entry) > 0) {
145                 set_init_entry(init_entry, "respawn");
146         }
147 }
148
149
150
151 void cleanup(int exitcode)
152 {
153 #ifdef HAVE_NEWT
154         newtCls();
155         newtRefresh();
156         newtFinished();
157 #endif
158         exit(exitcode);
159 }
160
161
162
163 void title(char *text)
164 {
165         if (setup_type == UI_TEXT) {
166                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
167         }
168 }
169
170
171
172 int yesno(char *question)
173 {
174 #ifdef HAVE_NEWT
175         newtComponent form = NULL;
176         newtComponent yesbutton = NULL;
177         newtComponent nobutton = NULL;
178         int i = 0;
179 #endif
180         int answer = 0;
181         char buf[SIZ];
182
183         switch (setup_type) {
184
185         case UI_TEXT:
186                 do {
187                         printf("%s\nYes/No --> ", question);
188                         fgets(buf, sizeof buf, stdin);
189                         answer = tolower(buf[0]);
190                         if (answer == 'y')
191                                 answer = 1;
192                         else if (answer == 'n')
193                                 answer = 0;
194                 } while ((answer < 0) || (answer > 1));
195                 break;
196
197 #ifdef HAVE_NEWT
198         case UI_NEWT:
199                 newtCenteredWindow(76, 10, "Question");
200                 form = newtForm(NULL, NULL, 0);
201                 for (i=0; i<num_tokens(question, '\n'); ++i) {
202                         extract_token(buf, question, i, '\n');
203                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
204                 }
205                 yesbutton = newtButton(10, 5, "Yes");
206                 nobutton = newtButton(60, 5, "No");
207                 newtFormAddComponent(form, yesbutton);
208                 newtFormAddComponent(form, nobutton);
209                 if (newtRunForm(form) == yesbutton) {
210                         answer = 1;
211                 }
212                 else {
213                         answer = 0;
214                 }
215                 newtPopWindow();
216                 newtFormDestroy(form);  
217
218                 break;
219 #endif
220
221         }
222         return (answer);
223 }
224
225 void set_value(char *prompt, char str[])
226 {
227 #ifdef HAVE_NEWT
228         newtComponent form;
229         char *result;
230         int i;
231 #endif
232         char buf[SIZ];
233         char setupmsg[SIZ];
234
235         strcpy(setupmsg, "");
236
237         switch (setup_type) {
238         case UI_TEXT:
239                 title("WebCit setup");
240                 printf("\n%s\n", prompt);
241                 printf("This is currently set to:\n%s\n", str);
242                 printf("Enter new value or press return to leave unchanged:\n");
243                 fgets(buf, sizeof buf, stdin);
244                 buf[strlen(buf) - 1] = 0;
245                 if (strlen(buf) != 0)
246                         strcpy(str, buf);
247                 break;
248 #ifdef HAVE_NEWT
249         case UI_NEWT:
250
251                 newtCenteredWindow(76, 10, "WebCit setup");
252                 form = newtForm(NULL, NULL, 0);
253                 for (i=0; i<num_tokens(prompt, '\n'); ++i) {
254                         extract_token(buf, prompt, i, '\n');
255                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
256                 }
257                 newtFormAddComponent(form, newtEntry(1, 8, str, 74, &result,
258                                         NEWT_FLAG_RETURNEXIT));
259                 newtRunForm(form);
260                 strcpy(str, result);
261
262                 newtPopWindow();
263                 newtFormDestroy(form);  
264
265 #endif
266         }
267 }
268
269
270 void important_message(char *title, char *msgtext)
271 {
272 #ifdef HAVE_NEWT
273         newtComponent form = NULL;
274         int i = 0;
275 #endif
276         char buf[SIZ];
277
278         switch (setup_type) {
279
280         case UI_TEXT:
281                 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");
282                 printf("       %s \n\n%s\n\n", title, msgtext);
283                 printf("Press return to continue...");
284                 fgets(buf, sizeof buf, stdin);
285                 break;
286
287 #ifdef HAVE_NEWT
288         case UI_NEWT:
289                 newtCenteredWindow(76, 10, title);
290                 form = newtForm(NULL, NULL, 0);
291                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
292                         extract_token(buf, msgtext, i, '\n');
293                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
294                 }
295                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
296                 newtRunForm(form);
297                 newtPopWindow();
298                 newtFormDestroy(form);  
299                 break;
300 #endif
301
302         }
303 }
304
305
306 void display_error(char *error_message)
307 {
308         important_message("Error", error_message);
309 }
310
311 void progress(char *text, long int curr, long int cmax)
312 {
313 #ifdef HAVE_NEWT
314
315         /* These variables are static because progress() gets called
316          * multiple times during the course of whatever operation is
317          * being performed.  This makes setup non-threadsafe, but who
318          * cares?
319          */
320         static newtComponent form = NULL;
321         static newtComponent scale = NULL;
322 #endif
323         static long dots_printed = 0L;
324         long a = 0;
325
326         switch (setup_type) {
327
328         case UI_TEXT:
329                 if (curr == 0) {
330                         printf("%s\n", text);
331                         printf("..........................");
332                         printf("..........................");
333                         printf("..........................\r");
334                         fflush(stdout);
335                         dots_printed = 0;
336                 } else if (curr == cmax) {
337                         printf("\r%79s\n", "");
338                 } else {
339                         a = (curr * 100) / cmax;
340                         a = a * 78;
341                         a = a / 100;
342                         while (dots_printed < a) {
343                                 printf("*");
344                                 ++dots_printed;
345                                 fflush(stdout);
346                         }
347                 }
348                 break;
349
350 #ifdef HAVE_NEWT
351         case UI_NEWT:
352                 if (curr == 0) {
353                         newtCenteredWindow(76, 8, text);
354                         form = newtForm(NULL, NULL, 0);
355                         scale = newtScale(1, 3, 74, cmax);
356                         newtFormAddComponent(form, scale);
357                         newtDrawForm(form);
358                         newtRefresh();
359                 }
360                 if ((curr > 0) && (curr <= cmax)) {
361                         newtScaleSet(scale, curr);
362                         newtRefresh();
363                 }
364                 if (curr == cmax) {
365                         newtFormDestroy(form);  
366                         newtPopWindow();
367                         newtRefresh();
368                 }
369                 break;
370 #endif
371
372         }
373 }
374
375
376
377
378 /*
379  * check_inittab_entry()  -- Make sure "webserver" is in /etc/inittab
380  *
381  */
382 void check_inittab_entry(void)
383 {
384         FILE *infp;
385         char buf[SIZ];
386         char looking_for[SIZ];
387         char question[SIZ];
388         char entryname[5];
389         char http_port[128];
390         char https_port[128];
391         char hostname[128];
392         char portname[128];
393
394         /* Determine the fully qualified path name of webserver */
395         snprintf(looking_for, sizeof looking_for, "%s/webserver ", WEBCITDIR);
396
397         /* If there's already an entry, then we have nothing left to do. */
398         if (strlen(init_entry) > 0) {
399                 return;
400         }
401
402         /* Otherwise, prompt the user to create an entry. */
403         snprintf(question, sizeof question,
404                 "There is no '%s' entry in /etc/inittab.\n"
405                 "Would you like to add one?",
406                 looking_for);
407         if (yesno(question) == 0)
408                 return;
409
410         snprintf(question, sizeof question,
411                 "On which port do you want WebCit to listen for HTTP "
412                 "requests?\n\nYou can use the standard port (80) if you are "
413                 "not running another\nweb server (such as Apache), otherwise "
414                 "select another port.");
415         sprintf(http_port, "2000");
416         set_value(question, http_port);
417
418 #ifdef HAVE_OPENSSL
419         snprintf(question, sizeof question,
420                 "On which port do you want WebCit to listen for HTTPS "
421                 "requests?\n\nYou can use the standard port (443) if you are "
422                 "not running another\nweb server (such as Apache), otherwise "
423                 "select another port.");
424         sprintf(https_port, "443");
425         set_value(question, https_port);
426 #endif
427
428         /* Find out where Citadel is. */
429         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
430                 strcpy(hostname, "uds");
431                 strcpy(portname, getenv("CITADEL"));
432         }
433         else {
434                 snprintf(question, sizeof question,
435                         "Is the Citadel service running on the same host as WebCit?");
436                 if (yesno(question)) {
437                         sprintf(hostname, "uds");
438                         sprintf(portname, "/usr/local/citadel");
439                         set_value("In what directory is Citadel installed?", portname);
440                 }
441                 else {
442                         sprintf(hostname, "127.0.0.1");
443                         sprintf(portname, "504");
444                         set_value("Enter the host name or IP address of your "
445                                 "Citadel server.", hostname);
446                         set_value("Enter the port number on which Citadel is "
447                                 "running (usually 504)", portname);
448                 }
449         }
450
451         /* Generate unique entry names for /etc/inittab */
452         snprintf(entryname, sizeof entryname, "c0");
453         do {
454                 ++entryname[1];
455                 if (entryname[1] > '9') {
456                         entryname[1] = 0;
457                         ++entryname[0];
458                         if (entryname[0] > 'z') {
459                                 display_error(
460                                    "Can't generate a unique entry name");
461                                 return;
462                         }
463                 }
464                 snprintf(buf, sizeof buf,
465                      "grep %s: /etc/inittab >/dev/null 2>&1", entryname);
466         } while (system(buf) == 0);
467         
468
469         /* Now write it out to /etc/inittab */
470         infp = fopen("/etc/inittab", "a");
471         if (infp == NULL) {
472                 display_error(strerror(errno));
473         } else {
474                 fprintf(infp, "# Start the WebCit server...\n");
475                 fprintf(infp, "h%s:2345:respawn:%s -p%s %s %s\n",
476                         entryname, looking_for,
477                         http_port, hostname, portname);
478 #ifdef HAVE_OPENSSL
479                 fprintf(infp, "s%s:2345:respawn:%s -p%s -s %s %s\n",
480                         entryname, looking_for,
481                         https_port, hostname, portname);
482 #endif
483                 fclose(infp);
484                 strcpy(init_entry, entryname);
485         }
486 }
487
488
489
490
491 /*
492  * Figure out what type of user interface we're going to use
493  */
494 int discover_ui(void)
495 {
496
497 #ifdef HAVE_NEWT
498         newtInit();
499         newtCls();
500         newtDrawRootText(0, 0, "WebCit Setup");
501         return UI_NEWT;
502 #endif
503         return UI_TEXT;
504 }
505
506
507
508
509
510 int main(int argc, char *argv[])
511 {
512         int a;
513         char aaa[128];
514         int info_only = 0;
515
516         /* set an invalid setup type */
517         setup_type = (-1);
518
519         /* Check to see if we're running the web installer */
520         if (getenv("CITADEL_INSTALLER") != NULL) {
521                 using_web_installer = 1;
522         }
523
524         /* parse command line args */
525         for (a = 0; a < argc; ++a) {
526                 if (!strncmp(argv[a], "-u", 2)) {
527                         strcpy(aaa, argv[a]);
528                         strcpy(aaa, &aaa[2]);
529                         setup_type = atoi(aaa);
530                 }
531                 if (!strcmp(argv[a], "-i")) {
532                         info_only = 1;
533                 }
534                 if (!strcmp(argv[a], "-q")) {
535                         setup_type = UI_SILENT;
536                 }
537         }
538
539
540         /* If a setup type was not specified, try to determine automatically
541          * the best one to use out of all available types.
542          */
543         if (setup_type < 0) {
544                 setup_type = discover_ui();
545         }
546         if (info_only == 1) {
547                 important_message("WebCit Setup", "Welcome to WebCit setup");
548                 cleanup(0);
549         }
550
551         /* Get started in a valid setup directory. */
552         strcpy(setup_directory, WEBCITDIR);
553         if ( (using_web_installer) && (getenv("WEBCIT") != NULL) ) {
554                 strcpy(setup_directory, getenv("WEBCIT"));
555         }
556         else {
557                 set_value("In what directory is WebCit installed?",
558                         setup_directory);
559         }
560         if (chdir(setup_directory) != 0) {
561                 important_message("WebCit Setup",
562                           "The directory you specified does not exist.");
563                 cleanup(errno);
564         }
565
566         /* See if we need to shut down the WebCit service. */
567         for (a=0; a<=3; ++a) {
568                 progress("Shutting down the WebCit service...", a, 3);
569                 if (a == 0) shutdown_service();
570                 sleep(1);
571         }
572
573         /* Now begin. */
574         switch (setup_type) {
575
576         case UI_TEXT:
577                 printf("\n\n\n"
578                         "               *** WebCit setup program ***\n\n");
579                 break;
580
581         }
582
583         check_inittab_entry();  /* Check /etc/inittab */
584
585         /* See if we can start the WebCit service. */
586         if (strlen(init_entry) > 0) {
587                 for (a=0; a<=3; ++a) {
588                         progress("Starting the WebCit service...", a, 3);
589                         if (a == 0) start_the_service();
590                         sleep(1);
591                 }
592                 important_message("Setup finished",
593                         "Setup is finished.  You may now log in.");
594         }
595         else {
596                 important_message("Setup finished",
597                         "Setup is finished.  You may now start the server.");
598         }
599
600         cleanup(0);
601         return 0;
602 }