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