]> andersk Git - moira.git/blob - dcm/dcm.pc
punt mrgdb
[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 "update.h"
14
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 EXEC SQL INCLUDE sqlca;
27 void sqlglm(char *, unsigned int *, unsigned int *);
28
29 RCSID("$Header$");
30
31 int generate_service(char *name, int force);
32 void do_hosts(char *service);
33 int dcm_send_file(char *service, char *host, char *target);
34 int dcm_execute(char *service, char *host, char *script);
35 void dbmserr(void);
36
37 EXEC SQL WHENEVER SQLERROR DO dbmserr();
38
39 #define SQL_NO_MATCH 1403
40 #define SOFT_FAIL(x) (((x) == MR_NO_MEM) || ((x) == MR_CANT_CONNECT) || ((x) == MR_CCONFIG) || ((x) == MR_DEADLOCK) || ((x) == MR_BUSY) || ((x) == MR_ABORT))
41
42 char whobuf[256], *whoami = whobuf, *db = "moira";
43
44 int main(int argc, char **argv)
45 {
46   int i;
47   EXEC SQL BEGIN DECLARE SECTION;
48   char buf[16], *name;
49   int enable;
50   EXEC SQL END DECLARE SECTION;
51   struct save_queue *sq;
52   int status;
53
54   if (strchr(argv[0], '/'))
55     strcpy(whoami, strrchr(argv[0], '/') + 1);
56   else strcpy(whoami, argv[0]);
57   umask(7);
58
59   setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
60   setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
61
62   initialize_sms_error_table();
63   initialize_krb_error_table();
64
65   /* if services were specified on the command line, do just those ones */
66   if (argc > 1)
67     {
68       for (i = 1; i < argc; i++)
69         {
70           if (generate_service(argv[i], 1))
71             do_hosts(argv[i]);
72         }
73       exit(0);
74     }
75
76   /* if DCM is not enabled, exit after logging */
77   if (!access(NODCMFILE, F_OK))
78     {
79       printf("/etc/nodcm exists -- exiting\n");
80       exit(1);
81     }
82
83   EXEC SQL CONNECT :db IDENTIFIED BY :db;
84
85   EXEC SQL SELECT value INTO :enable FROM numvalues WHERE name = 'dcm_enable';
86   if (enable == 0)
87     {
88       printf("dcm_enable not set -- exiting\n");
89       exit(1);
90     }
91
92   /* fetch list of services */
93   EXEC SQL DECLARE csr_svc CURSOR FOR SELECT LOWER(name) FROM servers
94     WHERE enable = 1 AND harderror = 0 AND update_int > 0;
95   EXEC SQL OPEN csr_svc;
96   sq = sq_create();
97   while (1)
98     {
99       EXEC SQL FETCH csr_svc INTO :buf;
100       if (sqlca.sqlcode)
101         break;
102
103       sq_save_data(sq, strdup(strtrim(buf)));
104     }
105   EXEC SQL CLOSE csr_svc;
106   /* we will repeatedly open and close the db since it seems to get
107      upset if you keep it open across a fork */
108   EXEC SQL COMMIT RELEASE;
109
110   /* Now run through list */
111   while (sq_get_data(sq, &name))
112     {
113       if (generate_service(name, 0))
114         {
115           switch (fork())
116             {
117             case -1:
118               fprintf(stderr, "dcm: could not fork for service %s -- exiting",
119                       name);
120               exit(1);
121             case 0:
122               sprintf(strchr(whoami, '\0'), " (%s)", name);
123               do_hosts(name);
124               com_err(whoami, 0, "exiting");
125               exit(0);
126             default:
127               break;
128             }
129         }
130     }
131
132   /* wait for children */
133   while (waitpid(0, &status, 0) > 0)
134     ;
135   com_err(whoami, 0, "exiting");
136   exit(0);
137 }
138
139 int generate_service(char *name, int force)
140 {
141   EXEC SQL BEGIN DECLARE SECTION;
142   int interval, dfcheck, status;
143   time_t now;
144   const char *errmsg;
145   EXEC SQL END DECLARE SECTION;
146   char dfgen_prog[64], dfgen_cmd[128];
147   struct sigaction action, prevaction;
148   int waits;
149
150   EXEC SQL CONNECT :db IDENTIFIED BY :db;
151
152   EXEC SQL SELECT update_int, dfcheck   INTO :interval, :dfcheck
153     FROM servers WHERE name = UPPER(:name);
154   if (sqlca.sqlcode == SQL_NO_MATCH)
155     {
156       com_err(whoami, 0, "No such service `%s'", name);
157       EXEC SQL COMMIT RELEASE;
158       return 0;
159     }
160
161   time(&now);
162
163   if ((interval * 60 + dfcheck < now) || force)
164     {
165       sprintf(dfgen_prog, "%s/%s.gen", BIN_DIR, name);
166       if (access(dfgen_prog, F_OK) != 0)
167         {
168           com_err(whoami, 0, "prog %s doesn't exist", dfgen_prog);
169           EXEC SQL COMMIT RELEASE;
170           return 0;
171         }
172       sprintf(dfgen_cmd, "exec %s %s/%s.out", dfgen_prog, DCM_DIR, name);
173       com_err(whoami, 0, "running %s", dfgen_prog);
174
175       EXEC SQL UPDATE servers SET inprogress = 1
176         WHERE name = UPPER(:name);
177
178       action.sa_flags = 0;
179       sigemptyset(&action.sa_mask);
180       action.sa_handler = SIG_DFL;
181       sigaction(SIGCHLD, &action, &prevaction);
182       waits = system(dfgen_cmd);
183       sigaction(SIGCHLD, &prevaction, NULL);
184       if (WIFSIGNALED(waits))
185         {
186           status = MR_COREDUMP;
187           com_err(whoami, status, " %s exited on signal %d",
188                   dfgen_prog, WTERMSIG(waits));
189         }
190       else if (WEXITSTATUS(waits))
191         {
192           /* extract the process's exit value */
193           status = WEXITSTATUS(waits) + ERROR_TABLE_BASE_sms;
194           if (status != MR_NO_CHANGE)
195             com_err(whoami, status, "in %s", dfgen_prog);
196         }
197       else
198         status = MR_SUCCESS;
199
200       if (status == MR_SUCCESS)
201         {
202           EXEC SQL UPDATE servers SET dfgen = :now, dfcheck = :now,
203             inprogress = 0 WHERE name = UPPER(:name);
204           EXEC SQL COMMIT RELEASE;
205           return 1;
206         }
207       else if (status == MR_NO_CHANGE)
208         {
209           EXEC SQL UPDATE servers SET dfcheck = :now, inprogress = 0
210             WHERE name = UPPER(:name);
211         }
212       else if (SOFT_FAIL(status))
213         {
214           errmsg = error_message(status);
215           EXEC SQL UPDATE servers SET errmsg = :errmsg, inprogress = 0
216             WHERE name = UPPER(:name);
217         }
218       else /* HARD_FAIL(status) */
219         {
220           errmsg = error_message(status);
221           EXEC SQL UPDATE servers SET dfcheck = :now, harderror = :status,
222             errmsg = :errmsg, inprogress = 0 WHERE name = UPPER(:name);
223           critical_alert("DCM", "DCM building config files for %s: %s",
224                          name, errmsg);
225         }
226     }
227   EXEC SQL COMMIT RELEASE;
228   return 0;
229 }
230
231 void do_hosts(char *service)
232 {
233   EXEC SQL BEGIN DECLARE SECTION;
234   char type[16], host[73], target[64], script[128];
235   const char *errmsg;
236   int status = 0, dfgen, replicated;
237   time_t now;
238   EXEC SQL END DECLARE SECTION;
239
240   time(&now);
241   mr_init();
242
243   EXEC SQL CONNECT :db IDENTIFIED BY :db;
244
245   EXEC SQL SELECT dfgen, type, target_file, script
246     INTO :dfgen, :type, :target, :script
247     FROM servers WHERE name = UPPER(:service);
248   replicated = !strncmp(type, "REPLICAT", 8);
249
250   EXEC SQL DECLARE csr_hst1 CURSOR FOR
251     SELECT m.name FROM machine m, serverhosts sh
252     WHERE sh.service = UPPER(:service)
253     AND sh.enable = 1 AND sh.hosterror = 0
254     AND sh.lts < :dfgen AND sh.mach_id = m.mach_id
255     FOR UPDATE OF sh.inprogress, sh.hosterror, sh.hosterrmsg;
256   EXEC SQL OPEN csr_hst1;
257
258   while (1)
259     {
260       EXEC SQL FETCH csr_hst1 INTO :host;
261       if (sqlca.sqlcode == SQL_NO_MATCH)
262         break;
263
264       com_err(whoami, 0, "sending %s data to %s", service, strtrim(host));
265       EXEC SQL UPDATE serverhosts SET inprogress = 1
266         WHERE CURRENT OF csr_hst1;
267       status = dcm_send_file(service, host, strtrim(target));
268       if (status)
269         {
270           errmsg = error_message(status);
271           EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
272             inprogress = 0 WHERE CURRENT OF csr_hst1;
273           if (!SOFT_FAIL(status))
274             {
275               EXEC SQL UPDATE serverhosts SET hosterror = :status
276                 WHERE CURRENT OF csr_hst1;
277               critical_alert("DCM", "DCM updating %s:%s: %s",
278                              service, host, errmsg);
279             }
280
281           if (replicated)
282             break;
283         }
284     }
285   EXEC SQL CLOSE csr_hst1;
286
287   if (status == MR_SUCCESS || !replicated)
288     {
289       EXEC SQL DECLARE csr_hst2 CURSOR FOR
290         SELECT m.name FROM machine m, serverhosts sh
291         WHERE sh.service = UPPER(:service) AND sh.inprogress = 1
292         AND sh.mach_id = m.mach_id
293         FOR UPDATE OF sh.hosterror, sh.hosterrmsg, sh.inprogress;
294       EXEC SQL OPEN csr_hst2;
295
296       while (1)
297         {
298           EXEC SQL FETCH csr_hst2 INTO :host;
299           if (sqlca.sqlcode == SQL_NO_MATCH)
300             break;
301
302           com_err(whoami, 0, "executing instructions on %s", strtrim(host));
303           status = dcm_execute(service, host, strtrim(script));
304           if (status)
305             {
306               errmsg = error_message(status);
307               EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
308                 inprogress = 0 WHERE CURRENT OF csr_hst2;
309               if (!SOFT_FAIL(status))
310                 {
311                   EXEC SQL UPDATE serverhosts SET hosterror = :status
312                     WHERE CURRENT OF csr_hst2;
313                   critical_alert("DCM", "DCM updating %s:%s: %s",
314                                  service, host, errmsg);
315                 }
316
317               if (replicated)
318                 {
319                   /* We're giving up, so clear the inprogress flag on
320                      any hosts in this service we haven't gotten to yet */
321                   EXEC SQL UPDATE serverhosts SET inprogress = 0
322                     WHERE service = UPPER(:service);
323                   break;
324                 }
325             }
326           else
327             {
328               EXEC SQL UPDATE serverhosts SET inprogress = 0, lts = :now
329                 WHERE CURRENT OF csr_hst2;
330             }
331         }
332       EXEC SQL CLOSE csr_hst2;
333     }
334
335   if (status && replicated)
336     {
337       EXEC SQL UPDATE servers SET harderror = :status, errmsg = :errmsg
338         WHERE name = UPPER(:service);
339     }
340
341   EXEC SQL COMMIT RELEASE;
342 }
343
344 int dcm_send_file(char *service, char *host, char *target)
345 {
346   char data[MAXPATHLEN];
347   int code, conn;
348
349   conn = mr_connect_internal(host, "moira_update");
350   if (!conn)
351     {
352       com_err(whoami, errno, "can't connect to %s", host);
353       return MR_CANT_CONNECT;
354     }
355
356   code = send_auth(conn, host);
357   if (code)
358     {
359       com_err(whoami, code, "authenticating to %s", host);
360       goto done;
361     }
362
363   sprintf(data, "%s/%s.out", DCM_DIR, service);
364   code = send_file(conn, data, target, 0);
365   if (code)
366     com_err(whoami, code, "sending data to %s", host);
367
368 done:
369   send_quit(conn);
370   close(conn);
371   return code;
372 }
373
374 int dcm_execute(char *service, char *host, char *script)
375 {
376   char inst[MAXPATHLEN];
377   int code, conn;
378
379   conn = mr_connect_internal(host, "moira_update");
380   if (!conn)
381     {
382       com_err(whoami, errno, "can't connect to %s", host);
383       return MR_CANT_CONNECT;
384     }
385
386   code = send_auth(conn, host);
387   if (code)
388     {
389       com_err(whoami, code, "authenticating to %s", host);
390       goto done;
391     }
392
393   sprintf(inst, "/tmp/moira-update.XXXXXX");
394   mktemp(inst);
395   code = send_file(conn, script, inst, 0);
396   if (code)
397     {
398       com_err(whoami, code, "sending instructions to %s", host);
399       goto done;
400     }
401
402   code = execute(conn, inst);
403   if (code)
404     com_err(whoami, code, "executing instructions on %s", host);
405
406 done:
407   send_quit(conn);
408   close(conn);
409   return code;
410 }
411
412 void dbmserr(void)
413 {
414   EXEC SQL BEGIN DECLARE SECTION;
415   char err_msg[256];
416   EXEC SQL END DECLARE SECTION;
417   int bufsize = 256, msglength = 0;
418
419   sqlglm(err_msg, &bufsize, &msglength);
420   err_msg[msglength] = '\0';
421   com_err(whoami, 0, "Encountered SQL error:\n%s", err_msg);
422   com_err(whoami, 0, "exiting");
423   exit(1);
424 }
This page took 0.064357 seconds and 5 git commands to generate.