]> andersk Git - moira.git/blob - dcm/dcm.pc
2f583fe32ad47ca15d550ebb2b9ec3097e9853f5
[moira.git] / dcm / dcm.pc
1 /* $Id$
2  *
3  * The Data Control Manager for Moira.
4  *
5  * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology.
6  * For copying and distribution information, see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include <moira.h>
12 #include <moira_site.h>
13 #include <moira_schema.h>
14 #include "update.h"
15
16 #include <sys/param.h>
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19
20 #include <errno.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 EXEC SQL INCLUDE sqlca;
28 void sqlglm(char *, unsigned int *, unsigned int *);
29
30 RCSID("$Header$");
31
32 int generate_service(char *name, int force);
33 void do_hosts(char *service);
34 int dcm_send_file(char *service, int type, char *host, char *target);
35 int dcm_execute(char *service, char *host, char *script);
36 void dbmserr(void);
37
38 #define SQL_NO_MATCH 1403
39 #define SOFT_FAIL(x) (((x) == MR_NO_MEM) || ((x) == MR_CANT_CONNECT) || ((x) == MR_CCONFIG) || ((x) == MR_DEADLOCK) || ((x) == MR_BUSY) || ((x) == MR_ABORT))
40
41 /* argument parsing macro */
42 #define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
43
44 char whobuf[256], *whoami = whobuf, *db = "moira";
45
46 enum { UNIQUE, DISTRIBUTED, REPLICATED };
47
48 int main(int argc, char **argv)
49 {
50   int i, force = 0;
51   EXEC SQL BEGIN DECLARE SECTION;
52   char buf[SERVERS_NAME_SIZE], *name;
53   int enable;
54   EXEC SQL END DECLARE SECTION;
55   struct save_queue *sq;
56   int status;
57   char **arg = argv;
58
59   if (strchr(argv[0], '/'))
60     strcpy(whoami, strrchr(argv[0], '/') + 1);
61   else strcpy(whoami, argv[0]);
62   umask(7);
63
64   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
65   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
66
67   initialize_sms_error_table();
68   initialize_krb_error_table();
69
70   while (++arg - argv < argc)
71     {
72       if (**arg == '-')
73         {
74           if (argis("f", "force"))
75             force++;
76           else
77             {
78               com_err(whoami, 0, "Usage: %s [-f] servicename", argv[0]);
79               exit(1);
80             }
81         }
82     }
83
84   /* if services were specified on the command line, do just those ones */
85   if (argc > 1)
86     {
87       for (i = 1; i < argc; i++)
88         {
89           if (argv[i][0] == '-')
90             continue;
91           if (generate_service(argv[i], force))
92             do_hosts(argv[i]);
93         }
94       exit(0);
95     }
96
97   /* if DCM is not enabled, exit after logging */
98   if (!access(NODCMFILE, F_OK))
99     {
100       printf("/etc/nodcm exists -- exiting\n");
101       exit(1);
102     }
103
104   EXEC SQL WHENEVER SQLERROR DO dbmserr();
105
106   EXEC SQL CONNECT :db IDENTIFIED BY :db;
107
108   EXEC SQL SELECT value INTO :enable FROM numvalues WHERE name = 'dcm_enable';
109   if (enable == 0)
110     {
111       printf("dcm_enable not set -- exiting\n");
112       exit(1);
113     }
114
115   /* fetch list of services */
116   EXEC SQL DECLARE csr_svc CURSOR FOR SELECT LOWER(name) FROM servers
117     WHERE enable = 1 AND harderror = 0 AND update_int > 0;
118   EXEC SQL OPEN csr_svc;
119   sq = sq_create();
120   while (1)
121     {
122       EXEC SQL FETCH csr_svc INTO :buf;
123       if (sqlca.sqlcode)
124         break;
125
126       sq_save_data(sq, strdup(strtrim(buf)));
127     }
128   EXEC SQL CLOSE csr_svc;
129   /* we will repeatedly open and close the db since it seems to get
130      upset if you keep it open across a fork */
131   EXEC SQL COMMIT RELEASE;
132
133   /* Now run through list */
134   while (sq_get_data(sq, &name))
135     {
136       if (generate_service(name, force))
137         {
138           switch (fork())
139             {
140             case -1:
141               com_err(whoami, errno, "forking for service %s -- exiting",
142                       name);
143               exit(1);
144             case 0:
145               sprintf(strchr(whoami, '\0'), " (%s:%ld)", name, (long)getpid());
146               do_hosts(name);
147               com_err(whoami, 0, "exiting");
148               exit(0);
149             default:
150               break;
151             }
152         }
153     }
154
155   com_err(whoami, 0, "All files generated. Waiting for children to exit");
156   while (waitpid(0, &status, 0) > 0)
157     ;
158   com_err(whoami, 0, "exiting");
159   exit(0);
160 }
161
162 int generate_service(char *name, int force)
163 {
164   EXEC SQL BEGIN DECLARE SECTION;
165   int interval, dfcheck, status, inprogress;
166   time_t now;
167   const char *errmsg;
168   EXEC SQL END DECLARE SECTION;
169   char dfgen_prog[MAXPATHLEN], dfgen_cmd[2 * MAXPATHLEN];
170   struct sigaction action, prevaction;
171   int waits;
172
173   EXEC SQL CONNECT :db IDENTIFIED BY :db;
174
175   EXEC SQL SELECT update_int, dfcheck, inprogress INTO :interval, :dfcheck,
176     :inprogress FROM servers WHERE name = UPPER(:name);
177   if (sqlca.sqlcode == SQL_NO_MATCH)
178     {
179       com_err(whoami, 0, "No such service `%s'", name);
180       EXEC SQL COMMIT RELEASE;
181       return 0;
182     }
183
184   /* Someone might try to run a DCM from the command line while the
185    * regular one is running, which will bypass the "interval" test.
186    * Check inprogress to make sure they don't stomp on themselves.
187    */
188   if (inprogress == 1)
189     {
190       com_err(whoami, 0, "DCM for service `%s' already in progress", name);
191       EXEC SQL COMMIT RELEASE;
192       return 0;
193     }
194
195   time(&now);
196
197   if ((interval * 60 + dfcheck < now) || force)
198     {
199       sprintf(dfgen_prog, "%s/%s.gen", BIN_DIR, name);
200       if (access(dfgen_prog, F_OK) != 0)
201         {
202           com_err(whoami, 0, "prog %s doesn't exist", dfgen_prog);
203           EXEC SQL COMMIT RELEASE;
204           return 0;
205         }
206       sprintf(dfgen_cmd, "exec %s %s/%s.out", dfgen_prog, DCM_DIR, name);
207       com_err(whoami, 0, "running %s", dfgen_prog);
208
209       EXEC SQL WHENEVER SQLERROR GOTO gen_cleanup;
210
211       EXEC SQL UPDATE servers SET inprogress = 1
212         WHERE name = UPPER(:name);
213       EXEC SQL COMMIT;
214
215       action.sa_flags = 0;
216       sigemptyset(&action.sa_mask);
217       action.sa_handler = SIG_DFL;
218       sigaction(SIGCHLD, &action, &prevaction);
219       waits = system(dfgen_cmd);
220       sigaction(SIGCHLD, &prevaction, NULL);
221       if (WIFSIGNALED(waits))
222         {
223           status = MR_COREDUMP;
224           com_err(whoami, status, " %s exited on signal %d",
225                   dfgen_prog, WTERMSIG(waits));
226         }
227       else if (WEXITSTATUS(waits))
228         {
229           /* extract the process's exit value */
230           status = WEXITSTATUS(waits) + ERROR_TABLE_BASE_sms;
231           if (status != MR_NO_CHANGE)
232             com_err(whoami, status, "in %s", dfgen_prog);
233         }
234       else
235         status = MR_SUCCESS;
236
237       if (status == MR_SUCCESS)
238         {
239           EXEC SQL UPDATE servers SET dfgen = :now, dfcheck = :now,
240             inprogress = 0 WHERE name = UPPER(:name);
241           EXEC SQL COMMIT RELEASE;
242           return 1;
243         }
244       else if (status == MR_NO_CHANGE)
245         {
246           EXEC SQL UPDATE servers SET dfcheck = :now, inprogress = 0
247             WHERE name = UPPER(:name);
248         }
249       else if (SOFT_FAIL(status))
250         {
251           errmsg = error_message(status);
252           EXEC SQL UPDATE servers SET errmsg = :errmsg, inprogress = 0
253             WHERE name = UPPER(:name);
254         }
255       else /* HARD_FAIL(status) */
256         {
257           errmsg = error_message(status);
258           EXEC SQL UPDATE servers SET harderror = :status, errmsg = :errmsg,
259             inprogress = 0 WHERE name = UPPER(:name);
260           critical_alert("DCM", "DCM building config files for %s: %s",
261                          name, errmsg);
262         }
263     }
264   else
265     {
266       com_err(whoami, 0, "DCM for service `%s' has run too recently.", name);
267       com_err(whoami, 0, "Use the -force flag to force a DCM.");
268     }
269
270   EXEC SQL COMMIT RELEASE;
271   return 0;
272
273 gen_cleanup:
274   EXEC SQL WHENEVER SQLERROR DO dbmserr(); 
275   EXEC SQL UPDATE servers SET inprogress = 0, harderror = MR_INTERNAL,
276     errmsg = 'DBMS Internal Error' WHERE name = UPPER(:name);
277   dbmserr();
278 }
279
280 void do_hosts(char *service)
281 {
282   EXEC SQL BEGIN DECLARE SECTION;
283   char server_type[SERVERS_TYPE_SIZE], host[MACHINE_NAME_SIZE], *name;
284   char target[SERVERS_TARGET_FILE_SIZE], script[SERVERS_SCRIPT_SIZE];
285   const char *errmsg;
286   int status = 0, dfgen, type, mid, inprogress;
287   time_t now;
288   EXEC SQL END DECLARE SECTION;
289   struct save_queue *sq;
290
291   time(&now);
292   mr_init();
293
294   EXEC SQL CONNECT :db IDENTIFIED BY :db;
295
296   EXEC SQL SELECT dfgen, type, target_file, script, inprogress
297     INTO :dfgen, :server_type, :target, :script, :inprogress
298     FROM servers WHERE name = UPPER(:service);
299   if (!strncmp(strtrim(server_type), "REPLICAT", 8))
300     type = REPLICATED;
301   else if (!strncmp(server_type, "DISTRIB", 8))
302     type = DISTRIBUTED;
303   else
304     type = UNIQUE;
305   strtrim(target);
306   strtrim(script);
307
308   if (inprogress == 1)
309     {
310       com_err(whoami, 0, "DCM for service `%s' already in progress", name);
311       EXEC SQL COMMIT RELEASE;
312       return;
313     }
314
315   EXEC SQL DECLARE csr_hst1 CURSOR FOR
316     SELECT m.name, m.mach_id FROM machine m, serverhosts sh
317     WHERE sh.service = UPPER(:service)
318     AND sh.enable = 1 AND sh.hosterror = 0
319     AND sh.lts < :dfgen AND sh.mach_id = m.mach_id;
320   EXEC SQL OPEN csr_hst1;
321   sq = sq_create();
322   while (1)
323     {
324       EXEC SQL FETCH csr_hst1 INTO :host, mid;
325       if (sqlca.sqlcode == SQL_NO_MATCH)
326         break;
327
328       sq_save_data(sq, strdup(strtrim(host)));
329       sq_save_data(sq, (void *)mid);
330     }
331   EXEC SQL CLOSE csr_hst1;
332
333   EXEC SQL WHENEVER SQLERROR GOTO host_cleanup;
334   while (sq_get_data(sq, &name))
335     {
336       sq_get_data(sq, &mid);
337
338       EXEC SQL SELECT inprogress INTO :inprogress FROM serverhosts
339         WHERE service = UPPER(:service) AND mach_id = :mid;
340       if (inprogress == 1)
341         {
342           com_err(whoami, 0, "DCM for service `%s' to host `%s' already in progress", service, name);
343           EXEC SQL COMMIT RELEASE;
344           return;
345         }
346
347       com_err(whoami, 0, "sending %s data to %s", service, name);
348       EXEC SQL UPDATE serverhosts SET inprogress = 1
349         WHERE service = UPPER(:service) AND mach_id = :mid;
350       EXEC SQL COMMIT;
351       status = dcm_send_file(service, type, name, target);
352       if (status)
353         {
354           errmsg = error_message(status);
355           EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
356             inprogress = 0, success = 0, ltt = :now
357             WHERE service = UPPER(:service) AND mach_id = :mid;
358           if (!SOFT_FAIL(status))
359             {
360               EXEC SQL UPDATE serverhosts SET hosterror = :status
361                 WHERE service = UPPER(:service) AND mach_id = :mid;
362               critical_alert("DCM", "DCM updating %s:%s: %s",
363                              service, name, errmsg);
364             }
365           EXEC SQL COMMIT;
366
367           if (type == REPLICATED)
368             break;
369         }
370     }
371   sq_destroy(sq);
372
373   if (status == MR_SUCCESS || type != REPLICATED)
374     {
375       EXEC SQL DECLARE csr_hst2 CURSOR FOR
376         SELECT m.name, m.mach_id FROM machine m, serverhosts sh
377         WHERE sh.service = UPPER(:service) AND sh.inprogress = 1
378         AND sh.enable = 1 AND sh.hosterror = 0 AND sh.mach_id = m.mach_id;
379       EXEC SQL OPEN csr_hst2;
380       sq = sq_create();
381
382       while (1)
383         {
384           EXEC SQL FETCH csr_hst2 INTO :host, :mid;
385           if (sqlca.sqlcode == SQL_NO_MATCH)
386             break;
387
388           sq_save_data(sq, strdup(strtrim(host)));
389           sq_save_data(sq, (void *)mid);
390         }
391       EXEC SQL CLOSE csr_hst2;
392
393       while (sq_get_data(sq, &name))
394         {
395           sq_get_data(sq, &mid);
396
397           com_err(whoami, 0, "executing instructions on %s", name);
398           status = dcm_execute(service, name, script);
399           if (status)
400             {
401               errmsg = error_message(status);
402               EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
403                 inprogress = 0, success = 0, ltt = :now
404                 WHERE service = UPPER(:service) AND mach_id = :mid;
405               if (!SOFT_FAIL(status))
406                 {
407                   EXEC SQL UPDATE serverhosts SET hosterror = :status
408                     WHERE service = UPPER(:service) AND mach_id = :mid;
409                   critical_alert("DCM", "DCM updating %s:%s: %s",
410                                  service, name, errmsg);
411                 }
412
413               if (type == REPLICATED)
414                 break;
415             }
416           else
417             {
418               EXEC SQL UPDATE serverhosts SET inprogress = 0, ltt = :now,
419                 lts = :now, success = 1 WHERE service = UPPER(:service)
420                 AND mach_id = :mid;
421             }
422           EXEC SQL COMMIT;
423         }
424       EXEC SQL CLOSE csr_hst2;
425     }
426
427   if (type == REPLICATED)
428     {
429       /* Clear inprogress flag on any hosts we started but didn't
430        * finish.
431        */
432       EXEC SQL UPDATE serverhosts SET inprogress = 0
433         WHERE service = UPPER(:service);
434     }
435
436   EXEC SQL WHENEVER SQLERROR DO dbmserr();
437   if (status && !SOFT_FAIL(status) && type == REPLICATED)
438     {
439       EXEC SQL UPDATE servers SET harderror = :status, errmsg = :errmsg
440         WHERE name = UPPER(:service);
441     }
442
443   EXEC SQL COMMIT RELEASE;
444   return;
445
446 host_cleanup:
447   EXEC SQL UPDATE serverhosts SET inprogress = 0, success = 0, ltt = :now,
448     hosterror = MR_INTERNAL, hosterrmsg = 'DBMS Internal Error'
449     WHERE service = UPPER(:service) AND mach_id = :mid;
450   if (type == REPLICATED)
451     {
452       EXEC SQL UPDATE servers SET harderror = MR_INTERNAL,
453         errmsg = 'DBMS Internal Error' WHERE name = UPPER(:service);
454     }
455 }
456
457 int dcm_send_file(char *service, int type, char *host, char *target)
458 {
459   char data[MAXPATHLEN];
460   int code, conn;
461
462   conn = mr_connect_internal(host, "moira_update");
463   if (!conn)
464     {
465       com_err(whoami, errno, "can't connect to %s", host);
466       return MR_CANT_CONNECT;
467     }
468
469   code = mr_send_auth(conn, host);
470   if (code)
471     {
472       com_err(whoami, code, "authenticating to %s", host);
473       goto done;
474     }
475
476   if (type == DISTRIBUTED)
477     sprintf(data, "%s/%s/%s", DCM_DIR, service, host);
478   else
479     sprintf(data, "%s/%s.out", DCM_DIR, service);
480   code = mr_send_file(conn, data, target, 0);
481   if (code)
482     com_err(whoami, code, "sending data to %s", host);
483
484 done:
485   mr_send_quit(conn);
486   close(conn);
487   return code;
488 }
489
490 int dcm_execute(char *service, char *host, char *script)
491 {
492   char inst[MAXPATHLEN];
493   int code, conn;
494
495   conn = mr_connect_internal(host, "moira_update");
496   if (!conn)
497     {
498       com_err(whoami, errno, "can't connect to %s", host);
499       return MR_CANT_CONNECT;
500     }
501
502   code = mr_send_auth(conn, host);
503   if (code)
504     {
505       com_err(whoami, code, "authenticating to %s", host);
506       goto done;
507     }
508
509   sprintf(inst, "/tmp/moira-update.XXXXXX");
510   mktemp(inst);
511   code = mr_send_file(conn, script, inst, 0);
512   if (code)
513     {
514       com_err(whoami, code, "sending instructions to %s", host);
515       goto done;
516     }
517
518   code = mr_execute(conn, inst);
519   if (code)
520     com_err(whoami, code, "executing instructions on %s", host);
521
522 done:
523   mr_send_quit(conn);
524   close(conn);
525   return code;
526 }
527
528 void dbmserr(void)
529 {
530   EXEC SQL BEGIN DECLARE SECTION;
531   char err_msg[256];
532   EXEC SQL END DECLARE SECTION;
533   int bufsize = 256, msglength = 0;
534
535   sqlglm(err_msg, &bufsize, &msglength);
536   err_msg[msglength] = '\0';
537   com_err(whoami, 0, "Encountered SQL error:\n%s", err_msg);
538   com_err(whoami, 0, "exiting");
539   exit(1);
540 }
This page took 0.094508 seconds and 3 git commands to generate.