bucoord: Don't malloc(0) if there's no work to do
[openafs.git] / src / bucoord / dump.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 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18
19 #include <afs/cmd.h>
20 #include <lwp.h>
21 #include <rx/rx.h>
22 #include <afs/bubasics.h>
23 #include <afs/butc.h>
24 #include <afs/com_err.h>
25 #include <lock.h>               /* for checking TC_ABORTFAILED. PA */
26 #include <afs/tcdata.h>         /* for checking TC_ABORTFAILED. PA */
27 #include <afs/butc.h>
28
29 #include "bc.h"
30 #include "error_macros.h"
31 #include "bucoord_internal.h"
32 #include "bucoord_prototypes.h"
33
34 struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
35
36 extern char *whoami;
37
38 extern afs_int32 lastTaskCode;
39
40 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
41
42 /* bc_Dumper
43  *      called (indirectly) to make a dump
44  * entry:
45  *      aindex - index into dumpTask array, contains all the information
46  *               relevant to the dump
47  */
48 int
49 bc_Dumper(int aindex)
50 {
51     struct rx_connection *tconn;
52     struct bc_volumeDump *tde;
53     afs_int32 count, port;
54     struct tc_dumpDesc *volDesc = NULL;
55     struct tc_dumpArray volArray;
56     char *baseNamePtr;
57     statusP statusPtr;
58
59     struct tc_dumpInterface dumpInterface;
60     struct tc_dumpInterface *tcdiPtr = &dumpInterface;
61     struct bc_dumpTask *dumpTaskPtr;
62
63     afs_int32 code = 0;
64
65     dumpTaskPtr = &bc_dumpTasks[aindex];
66
67     if (!dumpTaskPtr->portOffset || (dumpTaskPtr->portCount == 0))
68         port = 0;
69     else
70         port = dumpTaskPtr->portOffset[0];
71
72     code = ConnectButc(dumpTaskPtr->config, port, &tconn);
73     if (code)
74         return (code);
75
76     /* count number of volumes to be dumped and
77      * build array of volumes to be sent to backup system
78      */
79     for (count = 0, tde = dumpTaskPtr->volumes; tde;
80          tde = tde->next, count++);
81
82     /* Nothing to dump, so just return success */
83     if (count == 0)
84         goto error_exit;
85
86     volDesc = malloc(count * sizeof(struct tc_dumpDesc));
87     if (!volDesc) {
88         afs_com_err(whoami, BC_NOMEM, NULL);
89         ERROR(BC_NOMEM);
90     }
91
92     for (count = 0, tde = dumpTaskPtr->volumes; tde; tde = tde->next, count++) {
93         strcpy(volDesc[count].name, tde->name);
94         volDesc[count].vid = tde->vid;
95         volDesc[count].vtype = tde->volType;
96         volDesc[count].partition = tde->partition;
97         volDesc[count].hostAddr = HOSTADDR(&tde->server);       /* the internet address */
98         volDesc[count].date = tde->date;
99         volDesc[count].cloneDate = tde->cloneDate;      /* Not yet known */
100     }
101
102     volArray.tc_dumpArray_len = count;  /* element count */
103     volArray.tc_dumpArray_val = volDesc;        /* and data */
104
105     baseNamePtr = tailCompPtr(dumpTaskPtr->dumpName);
106
107     /* setup the interface structure */
108     memset(tcdiPtr, 0, sizeof(*tcdiPtr));
109
110     /* general */
111     strcpy(tcdiPtr->dumpPath, dumpTaskPtr->dumpName);
112     strcpy(tcdiPtr->volumeSetName, dumpTaskPtr->volSetName);
113
114     /* tapeset */
115     strcpy(tcdiPtr->tapeSet.format, dumpTaskPtr->volSetName);
116     strcat(tcdiPtr->tapeSet.format, ".");
117     strcat(tcdiPtr->tapeSet.format, baseNamePtr);
118     strcat(tcdiPtr->tapeSet.format, ".%d");
119     tcdiPtr->tapeSet.a = 1;
120     tcdiPtr->tapeSet.b = 1;
121     tcdiPtr->tapeSet.maxTapes = 1000000000;
122     tcdiPtr->tapeSet.expDate = dumpTaskPtr->expDate;    /* PA */
123     tcdiPtr->tapeSet.expType = dumpTaskPtr->expType;
124
125     /* construct dump set name */
126     strcpy(tcdiPtr->dumpName, dumpTaskPtr->volSetName);
127     strcat(tcdiPtr->dumpName, ".");
128     strcat(tcdiPtr->dumpName, baseNamePtr);
129
130     tcdiPtr->parentDumpId = dumpTaskPtr->parentDumpID;
131     tcdiPtr->dumpLevel = dumpTaskPtr->dumpLevel;
132     tcdiPtr->doAppend = dumpTaskPtr->doAppend;
133
134     /* start the dump on the tape coordinator */
135     printf("Starting dump\n");
136     code = TC_PerformDump(tconn, tcdiPtr, &volArray, &dumpTaskPtr->dumpID);
137     if (code) {
138         afs_com_err(whoami, code, "; Failed to start dump");
139         ERROR(code);
140     }
141
142     afs_com_err(whoami, 0, "Task %u: Dump (%s)", dumpTaskPtr->dumpID,
143             tcdiPtr->dumpName);
144
145     /* create status monitor block */
146     statusPtr = createStatusNode();
147     lock_Status();
148     statusPtr->taskId = dumpTaskPtr->dumpID;
149     statusPtr->port = port;
150     statusPtr->jobNumber = bc_jobNumber();
151     statusPtr->volsTotal = volArray.tc_dumpArray_len;
152     statusPtr->flags &= ~STARTING;
153     sprintf(statusPtr->taskName, "Dump (%s.%s)", dumpTaskPtr->volSetName,
154             baseNamePtr);
155     unlock_Status();
156
157   error_exit:
158     /* locally allocated resources */
159     if (volDesc)
160         free(volDesc);
161
162     if (tconn)
163         rx_DestroyConnection(tconn);
164
165     return (code);
166 }
167
168 /* freeDumpTaskVolumeList
169  *      free the list of volumes used for dumps
170  */
171
172 void
173 freeDumpTaskVolumeList(struct bc_volumeDump *vdptr)
174 {
175     struct bc_volumeDump *nextVdPtr;
176
177     while (vdptr != 0) {
178         nextVdPtr = vdptr->next;
179
180         if (vdptr->name)
181             free(vdptr->name);
182         free(vdptr);
183
184         vdptr = nextVdPtr;
185     }
186 }
187
188 /* bc_DmpRstStart
189  *     The other half of the dump/restore create process call. In bc_StartDmpRst,
190  *     we allocated a dumpTask entry. Here we do the task and then free the entry.
191  */
192 void *
193 bc_DmpRstStart(void *param)
194 {
195     afs_int32 aindex = (intptr_t)param;
196     struct bc_dumpTask *tdump;
197     afs_int32 code;
198
199     tdump = &bc_dumpTasks[aindex];
200
201     code = (tdump->callProc) (aindex);
202     if (code)
203         lastTaskCode = code;
204
205     /* Cleanup allocated data structures */
206     freeDumpTaskVolumeList(tdump->volumes);
207     tdump->dumpID = 0;
208     if (tdump->dumpName)
209         free(tdump->dumpName);
210     if (tdump->newExt)
211         free(tdump->newExt);
212     if (tdump->volSetName)
213         free(tdump->volSetName);
214     if (tdump->portOffset)
215         free(tdump->portOffset);
216     tdump->flags &= ~BC_DI_INUSE;
217
218     return (void *)(intptr_t)code;
219 }
220
221 /* bc_StartDmpRst
222  *      function to start dump running. Packages the relevant information
223  *      (from params) into any free dumpTask structure (globally allocated),
224  *      and then invokes bc_DmpRstStart to do the work, passing it a single
225  *      parameter, the index into the dumpTask array.
226  *
227  * entry:
228  *      aconfig - normally is bc_globalConfig
229  *      aproc - bc_Dumper for doing dumps
230  *              bc_Restorer for doing restores
231  */
232
233 int
234 bc_StartDmpRst(struct bc_config *aconfig, char *adname, char *avname,
235                struct bc_volumeDump *avolsToDump,
236                struct sockaddr_in *adestServer,
237                afs_int32 adestPartition, afs_int32 afromDate, char *anewExt,
238                int aoldFlag, afs_int32 aparent, afs_int32 alevel,
239                int (*aproc) (int), afs_int32 *ports, afs_int32 portCount,
240                struct bc_dumpSchedule *dsptr, int append, int dontExecute)
241 {
242     int i;
243     afs_int32 code;
244     PROCESS junk;
245
246     for (i = 0; i < BC_MAXSIMDUMPS; i++)
247         if (!(bc_dumpTasks[i].flags & BC_DI_INUSE))
248             break;
249
250     if (i >= BC_MAXSIMDUMPS) {
251         afs_com_err(whoami, BC_NOTLOCKED,
252                 "All of the dump/restore slots are in use, try again later");
253         return (BC_NOTLOCKED);
254     }
255
256     memset(&bc_dumpTasks[i], 0, sizeof(struct bc_dumpTask));
257     bc_dumpTasks[i].callProc = aproc;
258     bc_dumpTasks[i].config = aconfig;
259     bc_dumpTasks[i].volumes = avolsToDump;
260     bc_dumpTasks[i].flags = BC_DI_INUSE;
261     bc_dumpTasks[i].dumpName = bc_CopyString(adname);
262     bc_dumpTasks[i].volSetName = bc_CopyString(avname);
263     bc_dumpTasks[i].newExt = bc_CopyString(anewExt);
264     bc_dumpTasks[i].dumpLevel = alevel;
265     bc_dumpTasks[i].parentDumpID = aparent;
266     bc_dumpTasks[i].oldFlag = aoldFlag;
267     bc_dumpTasks[i].fromDate = afromDate;
268     bc_dumpTasks[i].destPartition = adestPartition;
269     bc_dumpTasks[i].portOffset = ports;
270     bc_dumpTasks[i].portCount = portCount;
271     bc_dumpTasks[i].doAppend = append;
272     bc_dumpTasks[i].dontExecute = dontExecute;
273
274     if (dsptr) {
275         /* This should be specified for dumps, but will be 0 for restores */
276         bc_dumpTasks[i].expDate = dsptr->expDate;
277         bc_dumpTasks[i].expType = dsptr->expType;
278     }
279     if (adestServer)
280         memcpy(&bc_dumpTasks[i].destServer, adestServer,
281                sizeof(struct sockaddr_in));
282     else
283         memset(&bc_dumpTasks[i].destServer, 0, sizeof(struct sockaddr_in));
284
285     code =
286         LWP_CreateProcess(bc_DmpRstStart, 20480, LWP_NORMAL_PRIORITY,
287                           (void *)(intptr_t)i, "helper", &junk);
288     if (code) {
289         bc_HandleMisc(code);
290         afs_com_err(whoami, code, "; Can't start thread");
291
292         /* Cleanup allocated data structures */
293         freeDumpTaskVolumeList(bc_dumpTasks[i].volumes);
294         bc_dumpTasks[i].dumpID = 0;
295         if (bc_dumpTasks[i].dumpName)
296             free(bc_dumpTasks[i].dumpName);
297         if (bc_dumpTasks[i].newExt)
298             free(bc_dumpTasks[i].newExt);
299         if (bc_dumpTasks[i].volSetName)
300             free(bc_dumpTasks[i].volSetName);
301         if (bc_dumpTasks[i].portOffset)
302             free(bc_dumpTasks[i].portOffset);
303         bc_dumpTasks[i].flags &= ~BC_DI_INUSE;
304         return (code);
305     }
306
307     return 0;
308 }
309
310 #ifdef notdef
311 /* bc_FindDumpSlot
312  *      Returns the dump slot of the dump with dumpID
313  * entry:
314  *      dumpID - id to look for
315  *      port - portoffset for tape coordinator
316  * exit:
317  *      0-n - i.e. 0 or positive number, is the dump slot
318  *      -1 - failed to find dumpID
319  */
320
321 afs_int32
322 bc_FindDumpSlot(afs_int32 dumpID, afs_int32 port)
323 {
324     int i;
325
326     for (i = 0; i < BC_MAXSIMDUMPS; i++) {
327         if ((bc_dumpTasks[i].flags & BC_DI_INUSE)
328             && (bc_dumpTasks[i].dumpID == dumpID)
329             && ((afs_int32) bc_dumpTasks[i].portOffset == port)) {
330             return (i);
331         }
332     }
333     return (-1);
334 }
335 #endif
336
337 /* bc_LabelTape
338  *      opens a connection to the tape coordinator and requests that it
339  *      label a tape
340  */
341
342 int
343 bc_LabelTape(char *afsname, char *pname, afs_int32 size,
344              struct bc_config *config, afs_int32 port)
345 {
346     struct rx_connection *tconn;
347     afs_int32 code = 0;
348     struct tc_tapeLabel label;
349     statusP statusPtr;
350     afs_uint32 taskId;
351
352     code = ConnectButc(config, port, &tconn);
353     if (code)
354         return (code);
355
356     memset(&label, 0, sizeof(label));
357     if (afsname)
358         strcpy(label.afsname, afsname);
359     if (pname)
360         strcpy(label.pname, (strcmp(pname, "") ? pname : "<NULL>"));
361     label.size = size;
362
363     code = TC_LabelTape(tconn, &label, &taskId);
364     if (code) {
365         afs_com_err(whoami, code, "; Failed to start labeltape");
366         return (code);
367     }
368
369     /* create status monitor block */
370     statusPtr = createStatusNode();
371     lock_Status();
372     statusPtr->taskId = taskId;
373     statusPtr->port = port;
374     statusPtr->jobNumber = bc_jobNumber();
375     /* statusPtr->flags    |= SILENT; *//* don't report termination */
376     statusPtr->flags &= ~STARTING;      /* ok to examine */
377
378     sprintf(statusPtr->taskName, "Labeltape (%s)",
379             (pname ? pname : (afsname ? afsname : "<NULL>")));
380     unlock_Status();
381
382     return 0;
383 }
384
385 /* bc_ReadLabel
386  *      open a connection to the tape coordinator and read the label on
387  *      a tape
388  */
389
390 int
391 bc_ReadLabel(struct bc_config *config, afs_int32 port)
392 {
393     struct rx_connection *tconn;
394     struct tc_tapeLabel label;
395     afs_uint32 taskId;
396     afs_int32 code = 0;
397     char *tname = 0;
398
399     code = ConnectButc(config, port, &tconn);
400     if (code)
401         return (code);
402
403     memset(&label, 0, sizeof(label));
404     code = TC_ReadLabel(tconn, &label, &taskId);
405     if (code) {
406         if (code == BUTM_NOLABEL) {
407             printf("Tape read was unlabelled\n");
408             return 0;
409         }
410         afs_com_err(whoami, code, "; Failed to start readlabel");
411         return (code);
412     }
413
414     if (strcmp(label.pname, ""))
415         tname = label.pname;
416     else if (strcmp(label.afsname, ""))
417         tname = label.afsname;
418
419     if (!tname) {
420         printf("Tape read was labelled : <NULL>  size : %u\n", label.size);
421     } else if (!label.tapeId) {
422         printf("Tape read was labelled : %s size : %lu Kbytes\n", tname,
423                (long unsigned int) label.size);
424     } else {
425         printf("Tape read was labelled : %s (%lu) size : %lu Kbytes\n", tname,
426                (long unsigned int) label.tapeId, (long unsigned int) label.size);
427     }
428
429     return 0;
430 }
431
432 int
433 bc_ScanDumps(struct bc_config *config, afs_int32 dbAddFlag, afs_int32 port)
434 {
435     struct rx_connection *tconn;
436     statusP statusPtr;
437     afs_uint32 taskId;
438     afs_int32 code = 0;
439
440     code = ConnectButc(config, port, &tconn);
441     if (code)
442         return (code);
443
444     code = TC_ScanDumps(tconn, dbAddFlag, &taskId);
445     if (code) {
446         afs_com_err(whoami, code, "; Failed to start scantape");
447         return (code);
448     }
449
450     /* create status monitor block */
451     statusPtr = createStatusNode();
452     lock_Status();
453     statusPtr->taskId = taskId;
454     statusPtr->port = port;
455     statusPtr->jobNumber = bc_jobNumber();
456     statusPtr->flags &= ~STARTING;      /* ok to examine */
457     sprintf(statusPtr->taskName, "Scantape");
458     unlock_Status();
459
460     return (0);
461 }
462
463 /*************/
464 /* utilities */
465 /*************/
466
467 /* get a connection to the tape controller */
468 afs_int32
469 bc_GetConn(struct bc_config *aconfig, afs_int32 aport,
470            struct rx_connection **tconn)
471 {
472     afs_uint32 host;
473     unsigned short port;
474     static struct rx_securityClass *rxsc;
475     struct bc_hostEntry *te;
476
477     *tconn = (struct rx_connection *)0;
478
479     /* use non-secure connections to butc */
480     if (!rxsc)
481         rxsc = rxnull_NewClientSecurityObject();
482     if (!rxsc || !aconfig)
483         return (-1);
484
485     for (te = aconfig->tapeHosts; te; te = te->next) {
486         if (te->portOffset == aport) {
487             /* found the right port */
488             host = te->addr.sin_addr.s_addr;
489             if (!host)
490                 return (BC_NOHOSTENTRY);        /* gethostbyname in bc_ParseHosts failed */
491
492             port = htons(BC_TAPEPORT + aport);
493
494             /* servers is 1; sec index is 0 */
495             *tconn = rx_NewConnection(host, port, 1, rxsc, 0);
496             return ((*tconn ? 0 : -1));
497         }
498     }
499     return (BC_NOHOSTENTRY);
500 }
501
502 /* CheckTCVersion
503  *      make sure we are talking to a compatible butc process.
504  * exit:
505  *      0 - ok
506  *      -1 - not compatible
507  */
508
509 int
510 CheckTCVersion(struct rx_connection *tconn)
511 {
512     struct tc_tcInfo tci;
513     afs_int32 code;
514
515     code = TC_TCInfo(tconn, &tci);
516     if (code)
517         return (code);
518
519     if (tci.tcVersion != CUR_BUTC_VERSION)
520         return (BC_VERSIONFAIL);
521
522     return 0;
523 }
524
525 int
526 ConnectButc(struct bc_config *config, afs_int32 port,
527             struct rx_connection **tconn)
528 {
529     afs_int32 code;
530
531     code = bc_GetConn(config, port, tconn);
532     if (code) {
533         afs_com_err(whoami, code,
534                 "; Can't connect to tape coordinator at port %d", port);
535         return (code);
536     }
537
538     code = CheckTCVersion(*tconn);
539     if (code) {
540         rx_DestroyConnection(*tconn);
541
542         if (code == BC_VERSIONFAIL)
543             afs_com_err(whoami, code,
544                     "; Backup and butc are not the same version");
545         else
546             afs_com_err(whoami, code,
547                     "; Can't access tape coordinator at port %d", port);
548
549         return (code);
550     }
551
552     return (0);
553 }