3 * The Data Control Manager for Moira.
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology.
6 * For copying and distribution information, see the file
10 #include <mit-copyright.h>
12 #include <moira_site.h>
15 #include <sys/param.h>
27 EXEC SQL INCLUDE sqlca;
28 void sqlglm(char *, unsigned int *, unsigned int *);
32 int generate_service(char *name, int force);
33 void do_hosts(char *service);
34 int dcm_send_file(char *service, char *host, char *target);
35 int dcm_execute(char *service, char *host, char *script);
38 EXEC SQL WHENEVER SQLERROR DO dbmserr();
40 #define SQL_NO_MATCH 1403
41 #define SOFT_FAIL(x) (((x) == MR_NO_MEM) || ((x) == MR_CANT_CONNECT) || ((x) == MR_CCONFIG) || ((x) == MR_DEADLOCK) || ((x) == MR_BUSY) || ((x) == MR_ABORT))
43 char whobuf[256], *whoami = whobuf, *db = "moira";
44 extern CONNECTION conn;
46 int main(int argc, char **argv)
49 EXEC SQL BEGIN DECLARE SECTION;
52 EXEC SQL END DECLARE SECTION;
53 struct save_queue *sq;
56 if (strchr(argv[0], '/'))
57 strcpy(whoami, strrchr(argv[0], '/') + 1);
58 else strcpy(whoami, argv[0]);
61 setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
62 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
64 initialize_sms_error_table();
65 initialize_krb_error_table();
67 /* if services were specified on the command line, do just those ones */
70 for (i = 1; i < argc; i++)
72 if (generate_service(argv[i], 1))
78 /* if DCM is not enabled, exit after logging */
79 if (!access(NODCMFILE, F_OK))
81 printf("/etc/nodcm exists -- exiting\n");
85 EXEC SQL CONNECT :db IDENTIFIED BY :db;
87 EXEC SQL SELECT value INTO :enable FROM numvalues WHERE name = 'dcm_enable';
90 printf("dcm_enable not set -- exiting\n");
94 /* fetch list of services */
95 EXEC SQL DECLARE csr_svc CURSOR FOR SELECT LOWER(name) FROM servers
96 WHERE enable = 1 AND harderror = 0 AND update_int > 0;
97 EXEC SQL OPEN csr_svc;
101 EXEC SQL FETCH csr_svc INTO :buf;
105 sq_save_data(sq, strdup(strtrim(buf)));
107 EXEC SQL CLOSE csr_svc;
108 /* we will repeatedly open and close the db since it seems to get
109 upset if you keep it open across a fork */
110 EXEC SQL COMMIT RELEASE;
112 /* Now run through list */
113 while (sq_get_data(sq, &name))
115 if (generate_service(name, 0))
120 fprintf(stderr, "dcm: could not fork for service %s -- exiting",
124 sprintf(strchr(whoami, '\0'), " (%s)", name);
126 com_err(whoami, 0, "exiting");
134 /* wait for children */
135 while (waitpid(0, &status, 0) > 0)
137 com_err(whoami, 0, "exiting");
141 int generate_service(char *name, int force)
143 EXEC SQL BEGIN DECLARE SECTION;
144 int interval, dfcheck, status;
147 EXEC SQL END DECLARE SECTION;
148 char dfgen_prog[64], dfgen_cmd[128];
149 struct sigaction action, prevaction;
152 EXEC SQL CONNECT :db IDENTIFIED BY :db;
154 EXEC SQL SELECT update_int, dfcheck INTO :interval, :dfcheck
155 FROM servers WHERE name = UPPER(:name);
156 if (sqlca.sqlcode == SQL_NO_MATCH)
158 com_err(whoami, 0, "No such service `%s'", name);
159 EXEC SQL COMMIT RELEASE;
165 if ((interval * 60 + dfcheck < now) || force)
167 sprintf(dfgen_prog, "%s/%s.gen", BIN_DIR, name);
168 if (access(dfgen_prog, F_OK) != 0)
170 com_err(whoami, 0, "prog %s doesn't exist", dfgen_prog);
171 EXEC SQL COMMIT RELEASE;
174 sprintf(dfgen_cmd, "exec %s %s/%s.out", dfgen_prog, DCM_DIR, name);
175 com_err(whoami, 0, "running %s", dfgen_prog);
177 EXEC SQL UPDATE servers SET inprogress = 1
178 WHERE name = UPPER(:name);
181 sigemptyset(&action.sa_mask);
182 action.sa_handler = SIG_DFL;
183 sigaction(SIGCHLD, &action, &prevaction);
184 waits = system(dfgen_cmd);
185 sigaction(SIGCHLD, &prevaction, NULL);
186 if (WIFSIGNALED(waits))
188 status = MR_COREDUMP;
189 com_err(whoami, status, " %s exited on signal %d",
190 dfgen_prog, WTERMSIG(waits));
192 else if (WEXITSTATUS(waits))
194 /* extract the process's exit value */
195 status = WEXITSTATUS(waits) + ERROR_TABLE_BASE_sms;
196 if (status != MR_NO_CHANGE)
197 com_err(whoami, status, "in %s", dfgen_prog);
202 if (status == MR_SUCCESS)
204 EXEC SQL UPDATE servers SET dfgen = :now, dfcheck = :now,
205 inprogress = 0 WHERE name = UPPER(:name);
206 EXEC SQL COMMIT RELEASE;
209 else if (status == MR_NO_CHANGE)
211 EXEC SQL UPDATE servers SET dfcheck = :now, inprogress = 0
212 WHERE name = UPPER(:name);
214 else if (SOFT_FAIL(status))
216 errmsg = error_message(status);
217 EXEC SQL UPDATE servers SET errmsg = :errmsg, inprogress = 0
218 WHERE name = UPPER(:name);
220 else /* HARD_FAIL(status) */
222 errmsg = error_message(status);
223 EXEC SQL UPDATE servers SET dfcheck = :now, harderror = :status,
224 errmsg = :errmsg, inprogress = 0 WHERE name = UPPER(:name);
225 critical_alert("DCM", "DCM building config files for %s: %s",
229 EXEC SQL COMMIT RELEASE;
233 void do_hosts(char *service)
235 EXEC SQL BEGIN DECLARE SECTION;
236 char type[16], host[73], target[64], script[128];
238 int status = 0, dfgen, replicated;
240 EXEC SQL END DECLARE SECTION;
245 EXEC SQL CONNECT :db IDENTIFIED BY :db;
247 EXEC SQL SELECT dfgen, type, target_file, script
248 INTO :dfgen, :type, :target, :script
249 FROM servers WHERE name = UPPER(:service);
250 replicated = !strncmp(type, "REPLICAT", 8);
252 EXEC SQL DECLARE csr_hst1 CURSOR FOR
253 SELECT m.name FROM machine m, serverhosts sh
254 WHERE sh.service = UPPER(:service)
255 AND sh.enable = 1 AND sh.hosterror = 0
256 AND sh.lts < :dfgen AND sh.mach_id = m.mach_id
257 FOR UPDATE OF sh.inprogress, sh.hosterror, sh.hosterrmsg;
258 EXEC SQL OPEN csr_hst1;
262 EXEC SQL FETCH csr_hst1 INTO :host;
263 if (sqlca.sqlcode == SQL_NO_MATCH)
266 com_err(whoami, 0, "sending %s data to %s", service, strtrim(host));
267 EXEC SQL UPDATE serverhosts SET inprogress = 1
268 WHERE CURRENT OF csr_hst1;
269 status = dcm_send_file(service, host, strtrim(target));
272 errmsg = error_message(status);
273 EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
274 inprogress = 0 WHERE CURRENT OF csr_hst1;
275 if (!SOFT_FAIL(status))
277 EXEC SQL UPDATE serverhosts SET hosterror = :status
278 WHERE CURRENT OF csr_hst1;
279 critical_alert("DCM", "DCM updating %s:%s: %s",
280 service, host, errmsg);
287 EXEC SQL CLOSE csr_hst1;
289 if (status == MR_SUCCESS || !replicated)
291 EXEC SQL DECLARE csr_hst2 CURSOR FOR
292 SELECT m.name FROM machine m, serverhosts sh
293 WHERE sh.service = UPPER(:service) AND sh.inprogress = 1
294 AND sh.mach_id = m.mach_id
295 FOR UPDATE OF sh.hosterror, sh.hosterrmsg, sh.inprogress;
296 EXEC SQL OPEN csr_hst2;
300 EXEC SQL FETCH csr_hst2 INTO :host;
301 if (sqlca.sqlcode == SQL_NO_MATCH)
304 com_err(whoami, 0, "executing instructions on %s", strtrim(host));
305 status = dcm_execute(service, host, strtrim(script));
308 errmsg = error_message(status);
309 EXEC SQL UPDATE serverhosts SET hosterrmsg = :errmsg,
310 inprogress = 0 WHERE CURRENT OF csr_hst2;
311 if (!SOFT_FAIL(status))
313 EXEC SQL UPDATE serverhosts SET hosterror = :status
314 WHERE CURRENT OF csr_hst2;
315 critical_alert("DCM", "DCM updating %s:%s: %s",
316 service, host, errmsg);
321 /* We're giving up, so clear the inprogress flag on
322 any hosts in this service we haven't gotten to yet */
323 EXEC SQL UPDATE serverhosts SET inprogress = 0
324 WHERE service = UPPER(:service);
330 EXEC SQL UPDATE serverhosts SET inprogress = 0, lts = :now
331 WHERE CURRENT OF csr_hst2;
334 EXEC SQL CLOSE csr_hst2;
337 if (status && replicated)
339 EXEC SQL UPDATE servers SET harderror = :status, errmsg = :errmsg
340 WHERE name = UPPER(:service);
343 EXEC SQL COMMIT RELEASE;
346 int dcm_send_file(char *service, char *host, char *target)
348 char addr[256], data[MAXPATHLEN];
351 sprintf(addr, "%s:moira_update", host);
352 conn = start_server_connection(addr, "");
353 if (!conn || (connection_status(conn) == CON_STOPPED))
355 com_err(whoami, connection_errno(conn), "can't connect to %s", addr);
356 return MR_CANT_CONNECT;
359 code = send_auth(host);
362 com_err(whoami, code, "authenticating to %s", host);
366 sprintf(data, "%s/%s.out", DCM_DIR, service);
367 code = send_file(data, target, 1);
368 if (code == MR_UNKNOWN_PROC)
369 code = send_file(data, target, 0);
371 com_err(whoami, code, "sending data to %s", host);
375 sever_connection(conn);
379 int dcm_execute(char *service, char *host, char *script)
381 char addr[256], inst[MAXPATHLEN];
384 sprintf(addr, "%s:moira_update", host);
385 conn = start_server_connection(addr, "");
386 if (!conn || (connection_status(conn) == CON_STOPPED))
388 com_err(whoami, connection_errno(conn), "can't connect to %s", addr);
389 return MR_CANT_CONNECT;
392 code = send_auth(host);
395 com_err(whoami, code, "authenticating to %s", host);
399 sprintf(inst, "/tmp/moira-update.XXXXXX");
401 code = send_file(script, inst, 0);
404 com_err(whoami, code, "sending instructions to %s", host);
408 code = execute(inst);
410 com_err(whoami, code, "executing instructions on %s", host);
414 sever_connection(conn);
420 EXEC SQL BEGIN DECLARE SECTION;
422 EXEC SQL END DECLARE SECTION;
423 int bufsize = 256, msglength = 0;
425 sqlglm(err_msg, &bufsize, &msglength);
426 err_msg[msglength] = '\0';
427 com_err(whoami, 0, "Encountered SQL error:\n%s", err_msg);
428 com_err(whoami, 0, "exiting");