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