265562cd82fca91f33df0a593e429883d37d7c04
[openafs.git] / src / bucoord / bc_status.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/stds.h>
12
13 #include <roken.h>
14
15 #include <sys/types.h>
16 #ifdef AFS_NT40_ENV
17 #include <winsock2.h>
18 #else
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #endif
23 #include <afs/com_err.h>
24 #include <afs/bubasics.h>
25 #include <lock.h>
26 #include <afs/tcdata.h>
27 #include <afs/cmd.h>
28 #include "bc.h"
29 #include "error_macros.h"
30 #include "bucoord_internal.h"
31 #include "bucoord_prototypes.h"
32
33 #define SET_FLAG(set)                           \
34     lock_Status();                              \
35     curPollPtr->flags |= (set);                 \
36     unlock_Status();
37
38 #define CLEAR_FLAG(clear)                       \
39     lock_Status();                              \
40     curPollPtr->flags &= ~(clear);              \
41     unlock_Status();
42
43 extern struct bc_config *bc_globalConfig;
44 extern afs_int32 bc_GetConn(struct bc_config *aconfig, afs_int32 aport, struct rx_connection **tconn);
45
46 /* globals for backup coordinator status management */
47
48 dlqlinkT statusHead;            /* chain of status blocks */
49 struct Lock statusQueueLock;    /* access control for status chain */
50 struct Lock cmdLineLock;        /* lock on the cmdLine */
51
52 afs_int32 lastTaskCode;         /* Error code from task that last finished */
53
54 /* nextItem
55  *      get next item for status interrogation, if any.
56  */
57 static statusP
58 nextItem(statusP linkPtr)
59 {
60     dlqlinkP ptr;
61
62     ptr = (dlqlinkP) linkPtr;
63
64     /* if last known item has terminated, reset ptr */
65     if (ptr == 0) {
66         ptr = &statusHead;
67         if (dlqEmpty(ptr))
68             return (0);
69     }
70
71     ptr = ptr->dlq_next;
72
73     /* if we're back at the head again */
74     if (ptr == &statusHead)
75         return (0);
76     return ((statusP) ptr);
77 }
78
79 #ifdef notdef
80 static statusP
81 nextItem(linkPtr)
82      statusP linkPtr;
83 {
84     dlqlinkP ptr;
85
86     ptr = (dlqlinkP) linkPtr;
87
88     /* if last known item has terminated, reset ptr */
89     if (ptr == 0) {
90         ptr = &statusHead;
91         if (dlqEmpty(ptr))
92             return (0);
93     }
94
95     ptr = ptr->dlq_next;
96
97     /* if we're back at the head again */
98     if (ptr == &statusHead) {
99         ptr = ptr->dlq_next;
100     }
101     return ((statusP) ptr);
102 }
103 #endif /* notdef */
104
105 char *cmdLine;
106
107 void *
108 cmdDispatch(void *unused)
109 {
110 #define MAXV    100
111     char *targv[MAXV];          /*Ptr to parsed argv stuff */
112     afs_int32 targc;            /*Num parsed arguments */
113     afs_int32 code;
114     char *internalCmdLine;
115
116     internalCmdLine = cmdLine;
117     unlock_cmdLine();
118
119     code = cmd_ParseLine(internalCmdLine, targv, &targc, MAXV);
120     if (code) {
121         printf("Couldn't parse line: '%s'", afs_error_message(code));
122         return (void *)(1);
123     }
124     free(internalCmdLine);
125
126     /*
127      * Because the "-at" option cannot be wildcarded, we cannot fall
128      * into recusive loop here by setting dispatchCount to 1.
129      */
130     doDispatch(targc, targv, 1);
131     cmd_FreeArgv(targv);
132     return(void *)(0);
133 }
134
135 void *
136 statusWatcher(void *unused)
137 {
138     struct rx_connection *tconn = NULL;
139     statusP curPollPtr = 0;
140
141     struct tciStatusS statusPtr;
142
143     /* task information */
144     afs_uint32 taskFlags;
145     afs_uint32 localTaskFlags;
146     afs_uint32 temp;            /* for flag manipulation */
147     afs_int32 jobNumber;
148     afs_int32 taskId;
149     afs_int32 port;
150     afs_int32 atTime;
151     PROCESS dispatchPid;
152
153     afs_int32 code = 0;
154
155     lastTaskCode = 0;
156
157     while (1) {                 /*w */
158         if (tconn)
159             rx_DestroyConnection(tconn);
160         tconn = NULL;
161
162         lock_Status();
163         curPollPtr = nextItem(curPollPtr);
164
165         if (curPollPtr == 0) {
166 #ifdef AFS_PTHREAD_ENV
167             struct timespec delaytime;
168             unlock_Status();
169             delayTime.tv_sec = 5;
170             delayTime.tv_nsec = 0;
171             pthread_delay_np(&delayTime);
172 #else
173             unlock_Status();
174             IOMGR_Sleep(5);     /* wait a while */
175 #endif /*else AFS_PTHREAD_ENV */
176             continue;
177         }
178
179         /* save useful information */
180         localTaskFlags = curPollPtr->flags;
181         taskId = curPollPtr->taskId;
182         port = curPollPtr->port;
183         atTime = curPollPtr->scheduledDump;
184         jobNumber = curPollPtr->jobNumber;
185         unlock_Status();
186
187         /* reset certain flags; local kill; */
188         CLEAR_FLAG(ABORT_LOCAL);
189
190         /* An abort request before the command even started */
191         if (atTime && (localTaskFlags & ABORT_REQUEST)) {
192             if (localTaskFlags & NOREMOVE) {
193                 curPollPtr->flags |= (STARTING | ABORT_DONE);   /* Will ignore on other passes */
194                 curPollPtr->scheduledDump = 0;
195             } else {
196                 deleteStatusNode(curPollPtr);
197             }
198             curPollPtr = 0;
199             continue;
200         }
201
202         /* A task not started yet - check its start time */
203         if (localTaskFlags & STARTING || atTime) {
204             /*
205              * Start a timed dump if its time has come.  When the job is
206              * started, it will allocate its own status structure so this
207              * one is no longer needed: delete it.
208              *
209              * Avoid multiple processes trouncing the cmdLine by placing
210              * lock around it.
211              */
212             if (atTime && (atTime <= time(0))) {
213                 lock_cmdLine(); /* Will unlock in cmdDispatch */
214
215                 cmdLine = curPollPtr->cmdLine;
216                 lock_Status();
217                 curPollPtr->cmdLine = 0;
218                 unlock_Status();
219
220                 printf("Starting scheduled dump: job %d\n", jobNumber);
221                 printf("schedD> %s\n", cmdLine);
222
223                 code =
224                     LWP_CreateProcess(cmdDispatch, 16384, LWP_NORMAL_PRIORITY,
225                                       (void *)2, "cmdDispatch", &dispatchPid);
226                 if (code) {
227                     if (cmdLine)
228                         free(cmdLine);
229                     unlock_cmdLine();
230                     printf("Couldn't create cmdDispatch task\n");
231                 }
232
233                 if (localTaskFlags & NOREMOVE) {
234                     curPollPtr->flags |= STARTING;      /* Will ignore on other passes */
235                     curPollPtr->flags |= (code ? TASK_ERROR : TASK_DONE);
236                     curPollPtr->scheduledDump = 0;
237                 } else {
238                     deleteStatusNode(curPollPtr);
239                 }
240                 curPollPtr = 0;
241             }
242             continue;
243         }
244
245         if (localTaskFlags & ABORT_LOCAL) {
246             /* kill the local task */
247             if ((localTaskFlags & CONTACT_LOST) != 0) {
248                 printf("Job %d: in contact with butc at port %d\n", jobNumber,
249                        port);
250                 printf("Job %d cont: Local kill ignored - use normal kill\n",
251                        jobNumber);
252             }
253         }
254
255         code = (afs_int32) bc_GetConn(bc_globalConfig, port, &tconn);
256         if (code) {
257             SET_FLAG(CONTACT_LOST);
258             continue;
259         }
260
261         if (CheckTCVersion(tconn)) {
262             SET_FLAG(CONTACT_LOST);
263             continue;
264         }
265
266         /* Send abort to TC requst if we have to */
267         if (localTaskFlags & ABORT_REQUEST) {
268             code = TC_RequestAbort(tconn, taskId);
269             if (code) {
270                 afs_com_err("statusWatcher", code, "; Can't post abort request");
271                 afs_com_err("statusWatcher", 0, "...Deleting job");
272                 if (localTaskFlags & NOREMOVE) {
273                     curPollPtr->flags |= (STARTING | TASK_ERROR);
274                     curPollPtr->scheduledDump = 0;
275                 } else {
276                     deleteStatusNode(curPollPtr);
277                 }
278                 curPollPtr = 0;
279                 continue;
280             } else {
281                 lock_Status();
282                 curPollPtr->flags &= ~ABORT_REQUEST;
283                 curPollPtr->flags |= ABORT_SENT;
284                 unlock_Status();
285             }
286         }
287
288         /* otherwise just get the status */
289         code = TC_GetStatus(tconn, taskId, &statusPtr);
290         if (code) {
291             if (code == TC_NODENOTFOUND) {
292                 printf("Job %d: %s - no such task on port %d, deleting\n",
293                        jobNumber, curPollPtr->taskName, port);
294
295                 if (localTaskFlags & NOREMOVE) {
296                     curPollPtr->flags |= (STARTING | TASK_ERROR);
297                     curPollPtr->scheduledDump = 0;
298                 } else {
299                     deleteStatusNode(curPollPtr);       /* delete this status node */
300                 }
301                 curPollPtr = 0;
302                 continue;
303             }
304
305             SET_FLAG(CONTACT_LOST);
306             continue;
307         }
308
309         /* in case we previously lost contact or couldn't find */
310         CLEAR_FLAG(CONTACT_LOST);
311
312         /* extract useful status */
313         taskFlags = statusPtr.flags;
314
315         /* update local status */
316         lock_Status();
317
318         /* remember some status flags in local struct */
319         temp =
320             (DRIVE_WAIT | OPR_WAIT | CALL_WAIT | TASK_DONE | ABORT_DONE |
321              TASK_ERROR);
322         curPollPtr->flags &= ~temp;     /* clear */
323         curPollPtr->flags |= (taskFlags & temp);        /* update */
324
325         curPollPtr->dbDumpId = statusPtr.dbDumpId;
326         curPollPtr->nKBytes = statusPtr.nKBytes;
327         strcpy(curPollPtr->volumeName, statusPtr.volumeName);
328         curPollPtr->volsFailed = statusPtr.volsFailed;
329         curPollPtr->lastPolled = statusPtr.lastPolled;
330         unlock_Status();
331
332         /* Are we done */
333         if (taskFlags & TASK_DONE) {    /*done */
334             if (taskFlags & ABORT_DONE) {
335                 if (curPollPtr->dbDumpId)
336                     printf("Job %d: %s: DumpID %u Aborted", jobNumber,
337                            curPollPtr->taskName, curPollPtr->dbDumpId);
338                 else
339                     printf("Job %d: %s Aborted", jobNumber,
340                            curPollPtr->taskName);
341
342                 if (taskFlags & TASK_ERROR)
343                     printf(" with errors\n");
344                 else
345                     printf("\n");
346
347                 lastTaskCode = 1;
348             }
349
350             else if (taskFlags & TASK_ERROR) {
351                 if (!(localTaskFlags & SILENT)) {
352                     if (curPollPtr->dbDumpId)
353                         printf("Job %d: DumpID %u Failed with errors\n",
354                                jobNumber, curPollPtr->dbDumpId);
355                     else
356                         printf("Job %d Failed with errors\n", jobNumber);
357                 }
358                 lastTaskCode = 2;
359             }
360
361             else {
362                 if (!(localTaskFlags & SILENT)) {
363                     if (curPollPtr->dbDumpId)
364                         printf("Job %d: %s: DumpID %u finished", jobNumber,
365                                curPollPtr->taskName, curPollPtr->dbDumpId);
366                     else
367                         printf("Job %d: %s finished", jobNumber,
368                                curPollPtr->taskName);
369
370                     if (curPollPtr->volsTotal) {
371                         printf(". %d volumes dumped",
372                                (curPollPtr->volsTotal -
373                                 curPollPtr->volsFailed));
374                         if (curPollPtr->volsFailed)
375                             printf(", %d failed", curPollPtr->volsFailed);
376                     }
377
378                     printf("\n");
379                 }
380                 lastTaskCode = 0;
381             }
382
383             /* make call to destroy task on server */
384             code = TC_EndStatus(tconn, taskId);
385             if (code)
386                 printf("Job %d: %s, error in job termination cleanup\n",
387                        jobNumber, curPollPtr->taskName);
388
389             if (localTaskFlags & NOREMOVE) {
390                 curPollPtr->flags |= STARTING;
391                 curPollPtr->scheduledDump = 0;
392             } else {
393                 deleteStatusNode(curPollPtr);   /* unlink and destroy local task */
394             }
395             curPollPtr = 0;
396         }                       /*done */
397     }                           /*w */
398     return NULL;
399 }
400
401 /* bc_jobNumber
402  *      Allocate a job number. Computes the maximum of all the job numbers
403  *      and then returns the maximum+1.
404  *      If no jobs are found, returns 1.
405  */
406
407 afs_int32
408 bc_jobNumber(void)
409 {
410     afs_int32 retval = 0;
411     dlqlinkP ptr;
412
413     ptr = statusHead.dlq_next;
414     while (ptr != &statusHead) {
415         /* compute max of all job numbers */
416         if (((statusP) ptr)->jobNumber > retval)
417             retval = ((statusP) ptr)->jobNumber;
418
419         ptr = ptr->dlq_next;
420     }
421     retval++;
422     return (retval);
423 }
424
425 /* waitForTask
426  *    Wait for a specific task to finish and then return.
427  *    Return the task's flags when it's done. If the job
428  *    had been cleaned up, then just return 0.
429  */
430 int
431 waitForTask(afs_uint32 taskId)
432 {
433     statusP ptr;
434     afs_int32 done = 0, rcode = 0, t;
435
436     t = (TASK_DONE | ABORT_DONE | TASK_ERROR);
437     while (!done) {
438         /* Sleep 2 seconds */
439 #ifdef AFS_PTHREAD_ENV
440         struct timespec delaytime;
441         delayTime.tv_sec = 2;
442         delayTime.tv_nsec = 0;
443         pthread_delay_np(&delayTime);
444 #else
445         IOMGR_Sleep(2);
446 #endif /*else AFS_PTHREAD_ENV */
447
448         /* Check if we are done */
449         lock_Status();
450         ptr = findStatus(taskId);
451         if (!ptr || (ptr->flags & t)) {
452             rcode = (ptr ? ptr->flags : 0);
453             done = 1;
454         }
455         unlock_Status();
456     }
457     return rcode;
458 }