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