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