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>
13 #include <moira_schema.h>
16 #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, int type, char *host, char *target);
35 int dcm_execute(char *service, char *host, char *script);
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))
41 char whobuf[256], *whoami = whobuf, *db = "moira";
43 enum { UNIQUE, DISTRIBUTED, REPLICATED };
45 int main(int argc, char **argv)
48 EXEC SQL BEGIN DECLARE SECTION;
49 char buf[SERVERS_NAME_SIZE], *name;
51 EXEC SQL END DECLARE SECTION;
52 struct save_queue *sq;
55 if (strchr(argv[0], '/'))
56 strcpy(whoami, strrchr(argv[0], '/') + 1);
57 else strcpy(whoami, argv[0]);
60 setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
61 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
63 initialize_sms_error_table();
64 initialize_krb_error_table();
66 /* if services were specified on the command line, do just those ones */
69 for (i = 1; i < argc; i++)
71 if (generate_service(argv[i], 1))
77 /* if DCM is not enabled, exit after logging */
78 if (!access(NODCMFILE, F_OK))
80 printf("/etc/nodcm exists -- exiting\n");
84 EXEC SQL WHENEVER SQLERROR DO dbmserr();
86 EXEC SQL CONNECT :db IDENTIFIED BY :db;
88 EXEC SQL SELECT value INTO :enable FROM numvalues WHERE name = 'dcm_enable';
91 printf("dcm_enable not set -- exiting\n");
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;
102 EXEC SQL FETCH csr_svc INTO :buf;
106 sq_save_data(sq, strdup(strtrim(buf)));
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;
113 /* Now run through list */
114 while (sq_get_data(sq, &name))
116 if (generate_service(name, 0))
121 com_err(whoami, errno, "forking for service %s -- exiting",
125 sprintf(strchr(whoami, '\0'), " (%s:%ld)", name, (long)getpid());
127 com_err(whoami, 0, "exiting");
135 com_err(whoami, 0, "All files generated. Waiting for children to exit");
136 while (waitpid(0, &status, 0) > 0)
138 com_err(whoami, 0, "exiting");
142 int generate_service(char *name, int force)
144 EXEC SQL BEGIN DECLARE SECTION;
145 int interval, dfcheck, status;
148 EXEC SQL END DECLARE SECTION;
149 char dfgen_prog[MAXPATHLEN], dfgen_cmd[2 * MAXPATHLEN];
150 struct sigaction action, prevaction;
153 EXEC SQL CONNECT :db IDENTIFIED BY :db;
155 EXEC SQL SELECT update_int, dfcheck INTO :interval, :dfcheck
156 FROM servers WHERE name = UPPER(:name);
157 if (sqlca.sqlcode == SQL_NO_MATCH)
159 com_err(whoami, 0, "No such service `%s'", name);
160 EXEC SQL COMMIT RELEASE;
166 if ((interval * 60 + dfcheck < now) || force)
168 sprintf(dfgen_prog, "%s/%s.gen", BIN_DIR, name);
169 if (access(dfgen_prog, F_OK) != 0)
171 com_err(whoami, 0, "prog %s doesn't exist", dfgen_prog);
172 EXEC SQL COMMIT RELEASE;
175 sprintf(dfgen_cmd, "exec %s %s/%s.out", dfgen_prog, DCM_DIR, name);
176 com_err(whoami, 0, "running %s", dfgen_prog);
178 EXEC SQL WHENEVER SQLERROR GOTO gen_cleanup;
180 EXEC SQL UPDATE servers SET inprogress = 1
181 WHERE name = UPPER(:name);
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))
192 status = MR_COREDUMP;
193 com_err(whoami, status, " %s exited on signal %d",
194 dfgen_prog, WTERMSIG(waits));
196 else if (WEXITSTATUS(waits))
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);
206 if (status == MR_SUCCESS)
208 EXEC SQL UPDATE servers SET dfgen = :now, dfcheck = :now,
209 inprogress = 0 WHERE name = UPPER(:name);
210 EXEC SQL COMMIT RELEASE;
213 else if (status == MR_NO_CHANGE)
215 EXEC SQL UPDATE servers SET dfcheck = :now, inprogress = 0
216 WHERE name = UPPER(:name);
218 else if (SOFT_FAIL(status))
220 errmsg = error_message(status);
221 EXEC SQL UPDATE servers SET errmsg = :errmsg, inprogress = 0
222 WHERE name = UPPER(:name);
224 else /* HARD_FAIL(status) */
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",
233 EXEC SQL COMMIT RELEASE;
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);
243 void do_hosts(char *service)
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];
249 int status = 0, dfgen, type, mid;
251 EXEC SQL END DECLARE SECTION;
252 struct save_queue *sq;
257 EXEC SQL CONNECT :db IDENTIFIED BY :db;
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))
264 else if (!strncmp(server_type, "DISTRIB", 8))
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;
280 EXEC SQL FETCH csr_hst1 INTO :host, mid;
281 if (sqlca.sqlcode == SQL_NO_MATCH)
284 sq_save_data(sq, strdup(strtrim(host)));
285 sq_save_data(sq, (void *)mid);
287 EXEC SQL CLOSE csr_hst1;
289 EXEC SQL WHENEVER SQLERROR GOTO host_cleanup;
290 while (sq_get_data(sq, &name))
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;
297 status = dcm_send_file(service, type, name, target);
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))
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);
313 if (type == REPLICATED)
319 if (status == MR_SUCCESS || type != REPLICATED)
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;
330 EXEC SQL FETCH csr_hst2 INTO :host, :mid;
331 if (sqlca.sqlcode == SQL_NO_MATCH)
334 sq_save_data(sq, strdup(strtrim(host)));
335 sq_save_data(sq, (void *)mid);
337 EXEC SQL CLOSE csr_hst2;
339 while (sq_get_data(sq, &name))
341 sq_get_data(sq, &mid);
343 com_err(whoami, 0, "executing instructions on %s", name);
344 status = dcm_execute(service, name, script);
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))
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);
359 if (type == REPLICATED)
364 EXEC SQL UPDATE serverhosts SET inprogress = 0, ltt = :now,
365 lts = :now, success = 1 WHERE service = UPPER(:service)
370 EXEC SQL CLOSE csr_hst2;
373 if (type == REPLICATED)
375 /* Clear inprogress flag on any hosts we started but didn't
378 EXEC SQL UPDATE serverhosts SET inprogress = 0
379 WHERE service = UPPER(:service);
382 EXEC SQL WHENEVER SQLERROR DO dbmserr();
383 if (status && !SOFT_FAIL(status) && type == REPLICATED)
385 EXEC SQL UPDATE servers SET harderror = :status, errmsg = :errmsg
386 WHERE name = UPPER(:service);
389 EXEC SQL COMMIT RELEASE;
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)
398 EXEC SQL UPDATE servers SET harderror = MR_INTERNAL,
399 errmsg = 'DBMS Internal Error' WHERE name = UPPER(:service);
403 int dcm_send_file(char *service, int type, char *host, char *target)
405 char data[MAXPATHLEN];
408 conn = mr_connect_internal(host, "moira_update");
411 com_err(whoami, errno, "can't connect to %s", host);
412 return MR_CANT_CONNECT;
415 code = mr_send_auth(conn, host);
418 com_err(whoami, code, "authenticating to %s", host);
422 if (type == DISTRIBUTED)
423 sprintf(data, "%s/%s/%s", DCM_DIR, service, host);
425 sprintf(data, "%s/%s.out", DCM_DIR, service);
426 code = mr_send_file(conn, data, target, 0);
428 com_err(whoami, code, "sending data to %s", host);
436 int dcm_execute(char *service, char *host, char *script)
438 char inst[MAXPATHLEN];
441 conn = mr_connect_internal(host, "moira_update");
444 com_err(whoami, errno, "can't connect to %s", host);
445 return MR_CANT_CONNECT;
448 code = mr_send_auth(conn, host);
451 com_err(whoami, code, "authenticating to %s", host);
455 sprintf(inst, "/tmp/moira-update.XXXXXX");
457 code = mr_send_file(conn, script, inst, 0);
460 com_err(whoami, code, "sending instructions to %s", host);
464 code = mr_execute(conn, inst);
466 com_err(whoami, code, "executing instructions on %s", host);
476 EXEC SQL BEGIN DECLARE SECTION;
478 EXEC SQL END DECLARE SECTION;
479 int bufsize = 256, msglength = 0;
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");