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