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