9a0cff0c11b850aebba1883fcb9a7681a48c03bb
[debian/amanda] / server-src / changer.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: changer.c,v 1.14.4.6.4.1.2.3 2003/02/28 19:23:25 martinea Exp $
28  *
29  * interface routines for tape changers
30  */
31 #include "amanda.h"
32 #include "conffile.h"
33 #include "version.h"
34
35 #include "changer.h"
36
37 /*
38  * If we don't have the new-style wait access functions, use our own,
39  * compatible with old-style BSD systems at least.  Note that we don't
40  * care about the case w_stopval == WSTOPPED since we don't ask to see
41  * stopped processes, so should never get them from wait.
42  */
43 #ifndef WEXITSTATUS
44 #   define WEXITSTATUS(r)       (((union wait *) &(r))->w_retcode)
45 #   define WTERMSIG(r)          (((union wait *) &(r))->w_termsig)
46
47 #   undef  WIFSIGNALED
48 #   define WIFSIGNALED(r)       (((union wait *) &(r))->w_termsig != 0)
49 #endif
50
51
52 int changer_debug = 0;
53 char *changer_resultstr = NULL;
54
55 static char *tapechanger = NULL;
56
57 /* local functions */
58 static int changer_command P((char *cmd, char *arg));
59
60 int changer_init()
61 {
62     tapechanger = getconf_str(CNF_TPCHANGER);
63     return strcmp(tapechanger, "") != 0;
64 }
65
66
67 static int report_bad_resultstr()
68 {
69     char *s;
70
71     s = vstralloc("badly formed result from changer: ",
72                   "\"", changer_resultstr, "\"",
73                   NULL);
74     amfree(changer_resultstr);
75     changer_resultstr = s;
76     return 2;
77 }
78
79 static int run_changer_command(cmd, arg, slotstr, rest)
80 char *cmd;
81 char *arg;
82 char **slotstr;
83 char **rest;
84 {
85     int exitcode;
86     char *result_copy;
87     char *slot;
88     char *s;
89     int ch;
90
91     *slotstr = NULL;
92     *rest = NULL;
93     exitcode = changer_command(cmd, arg);
94     s = changer_resultstr;
95     ch = *s++;
96
97     skip_whitespace(s, ch);
98     if(ch == '\0') return report_bad_resultstr();
99     slot = s - 1;
100     skip_non_whitespace(s, ch);
101     s[-1] = '\0';
102     *slotstr = newstralloc(*slotstr, slot);
103     s[-1] = ch;
104
105     skip_whitespace(s, ch);
106     *rest = s - 1;
107
108     if(exitcode) {
109         if(ch == '\0') return report_bad_resultstr();
110         result_copy = stralloc(s - 1);
111         amfree(changer_resultstr);
112         changer_resultstr = result_copy;
113         return exitcode;
114     }
115     return 0;
116 }
117
118 int changer_reset(slotstr)
119 char **slotstr;
120 {
121     char *rest;
122
123     return run_changer_command("-reset", (char *) NULL, slotstr, &rest);
124 }
125
126 int changer_clean(slotstr)
127 char **slotstr;
128 {
129     char *rest;
130
131     return run_changer_command("-clean", (char *) NULL, slotstr, &rest);
132 }
133
134 int changer_eject(slotstr)
135 char **slotstr;
136 {
137     char *rest;
138
139     return run_changer_command("-eject", (char *) NULL, slotstr, &rest);
140 }
141
142 int changer_loadslot(inslotstr, outslotstr, devicename)
143 char *inslotstr, **outslotstr, **devicename;
144 {
145     char *rest;
146     int rc;
147
148     rc = run_changer_command("-slot", inslotstr, outslotstr, &rest);
149     if(rc) return rc;
150
151     if(*rest == '\0') return report_bad_resultstr();
152
153     *devicename = newstralloc(*devicename, rest);
154     return 0;
155 }
156
157 /* This function is somewhat equal to changer_info with one additional
158    parameter, to get information, if the changer is able to search for
159    tapelabels himself. E.g. Barcodereader
160    The changer_script answers with an additional parameter, if it is able
161    to search. This one should be 1, if it is able to search, and 0 if it
162    knows about the extension. If the additional answer is omitted, the
163    changer is not able to search for a tape. 
164 */
165 int changer_query(nslotsp, curslotstr, backwardsp, searchable)
166 int *nslotsp, *backwardsp, *searchable;
167 char **curslotstr;
168 {
169     char *rest;
170     int rc;
171
172     rc = run_changer_command("-info", (char *) NULL, curslotstr, &rest);
173     if(rc) return rc;
174
175     dbprintf(("changer_query: changer return was %s\n",rest));
176     if (sscanf(rest, "%d %d %d", nslotsp, backwardsp, searchable) != 3) {
177       if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
178         return report_bad_resultstr();
179       } else {
180         *searchable = 0;
181       }
182     }
183     dbprintf(("changer_query: searchable = %d\n",*searchable));
184     return 0;
185 }
186
187 int changer_info(nslotsp, curslotstr, backwardsp)
188 int *nslotsp, *backwardsp;
189 char **curslotstr;
190 {
191     char *rest;
192     int rc;
193
194     rc = run_changer_command("-info", (char *) NULL, curslotstr, &rest);
195     if(rc) return rc;
196
197     if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
198         return report_bad_resultstr();
199     }
200     return 0;
201 }
202
203
204 /* ---------------------------- */
205
206 void changer_scan(user_init, user_slot)
207 int (*user_init) P((int rc, int nslots, int backwards));
208 int (*user_slot) P((int rc, char *slotstr, char *device));
209 {
210     char *slotstr, *device = NULL, *curslotstr = NULL;
211     int nslots, checked, backwards, rc, done;
212
213     rc = changer_info(&nslots, &curslotstr, &backwards);
214     done = user_init(rc, nslots, backwards);
215     amfree(curslotstr);
216
217     slotstr = "current";
218     checked = 0;
219
220     while(!done && checked < nslots) {
221         rc = changer_loadslot(slotstr, &curslotstr, &device);
222         if(rc > 0)
223             done = user_slot(rc, curslotstr, device);
224         else if(!done)
225             done = user_slot(0,  curslotstr, device);
226         amfree(curslotstr);
227         amfree(device);
228
229         checked += 1;
230         slotstr = "next";
231     }
232 }
233
234 /* This function first uses searchlabel and changer_search, if
235    the library is able to find a tape itself. If it is not, or if 
236    the tape could not be found, then the normal scan is done like 
237    in changer_scan.
238 */
239 void changer_find(user_init, user_slot, searchlabel)
240 int (*user_init) P((int rc, int nslots, int backwards));
241 int (*user_slot) P((int rc, char *slotstr, char *device));
242 char *searchlabel;
243 {
244     char *slotstr, *device = NULL, *curslotstr = NULL;
245     int nslots, checked, backwards, rc, done, searchable;
246
247     rc = changer_query(&nslots, &curslotstr, &backwards, &searchable);
248     done = user_init(rc, nslots, backwards);
249     amfree(curslotstr);
250    
251     if (searchlabel != NULL)
252     {
253       dbprintf(("changer_find: looking for %s changer is searchable = %d\n",
254                 searchlabel, searchable));
255     } else {
256       dbprintf(("changer_find: looking for NULL changer is searchable = %d\n",
257                 searchable));
258     }
259
260     if ((searchlabel!=NULL) && searchable && !done){
261       rc=changer_search(searchlabel,&curslotstr,&device);
262       if(rc == 0)
263         done = user_slot(rc,curslotstr,device);
264     }
265  
266     slotstr = "current";
267     checked = 0;
268
269     while(!done && checked < nslots) {
270         rc = changer_loadslot(slotstr, &curslotstr, &device);
271         if(rc > 0)
272             done = user_slot(rc, curslotstr, device);
273         else if(!done)
274             done = user_slot(0,  curslotstr, device);
275         amfree(curslotstr);
276         amfree(device);
277
278         checked += 1;
279         slotstr = "next";
280     }
281 }
282
283 /* ---------------------------- */
284
285 void changer_current(user_init, user_slot)
286 int (*user_init) P((int rc, int nslots, int backwards));
287 int (*user_slot) P((int rc, char *slotstr, char *device));
288 {
289     char *device = NULL, *curslotstr = NULL;
290     int nslots, backwards, rc, done;
291
292     rc = changer_info(&nslots, &curslotstr, &backwards);
293     done = user_init(rc, nslots, backwards);
294     amfree(curslotstr);
295
296     rc = changer_loadslot("current", &curslotstr, &device);
297     if(rc > 0) {
298         done = user_slot(rc, curslotstr, device);
299     } else if(!done) {
300         done = user_slot(0,  curslotstr, device);
301     }
302     amfree(curslotstr);
303     amfree(device);
304 }
305
306 /* ---------------------------- */
307
308 static int changer_command(cmd, arg)
309      char *cmd;
310      char *arg;
311 {
312     int fd[2];
313     amwait_t wait_exitcode;
314     int exitcode;
315     char num1[NUM_STR_SIZE];
316     char num2[NUM_STR_SIZE];
317     char *cmdstr;
318     pid_t pid, changer_pid;
319
320     if (*tapechanger != '/') {
321         tapechanger = vstralloc(libexecdir, "/", tapechanger, versionsuffix(),
322                                 NULL);
323         malloc_mark(tapechanger);
324     }
325     cmdstr = vstralloc(tapechanger, " ",
326                        cmd, arg ? " " : "", 
327                        arg ? arg : "",
328                        NULL);
329
330     if(changer_debug) {
331         fprintf(stderr, "changer: opening pipe to: %s\n", cmdstr);
332         fflush(stderr);
333     }
334
335     amfree(changer_resultstr);
336
337     if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
338         changer_resultstr = vstralloc ("<error> ",
339                                        "could not create pipe for \"",
340                                        cmdstr,
341                                        "\": ",
342                                        strerror(errno),
343                                        NULL);
344         exitcode = 2;
345         goto failed;
346     }
347     if(fd[0] < 0 || fd[0] >= FD_SETSIZE) {
348         ap_snprintf(num1, sizeof(num1), "%d", fd[0]);
349         ap_snprintf(num2, sizeof(num2), "%d", FD_SETSIZE-1);
350         changer_resultstr = vstralloc ("<error> ",
351                                        "could not create pipe for \"",
352                                        cmdstr,
353                                        "\": ",
354                                        "socketpair 0: descriptor ",
355                                        num1,
356                                        " out of range ( .. ",
357                                        num2,
358                                        ")",
359                                        NULL);
360         exitcode = 2;
361         goto done;
362     }
363     if(fd[1] < 0 || fd[1] >= FD_SETSIZE) {
364         ap_snprintf(num1, sizeof(num1), "%d", fd[1]);
365         ap_snprintf(num2, sizeof(num2), "%d", FD_SETSIZE-1);
366         changer_resultstr = vstralloc ("<error> ",
367                                        "could not create pipe for \"",
368                                        cmdstr,
369                                        "\": ",
370                                        "socketpair 1: descriptor ",
371                                        num1,
372                                        " out of range ( .. ",
373                                        num2,
374                                        ")",
375                                        NULL);
376         exitcode = 2;
377         goto done;
378     }
379
380     switch(changer_pid = fork()) {
381     case -1:
382         changer_resultstr = vstralloc ("<error> ",
383                                        "could not fork for \"",
384                                        cmdstr,
385                                        "\": ",
386                                        strerror(errno),
387                                        NULL);
388         exitcode = 2;
389         goto done;
390     case 0:
391         if(dup2(fd[1], 1) == -1 || dup2(fd[1], 2) == -1) {
392             changer_resultstr = vstralloc ("<error> ",
393                                            "could not open pipe to \"",
394                                            cmdstr,
395                                            "\": ",
396                                            strerror(errno),
397                                            NULL);
398             (void)write(fd[1], changer_resultstr, strlen(changer_resultstr));
399             exit(1);
400         }
401         aclose(fd[0]);
402         aclose(fd[1]);
403         if(config_dir && chdir(config_dir) == -1) {
404             changer_resultstr = vstralloc ("<error> ",
405                                            "could not cd to \"",
406                                            config_dir,
407                                            "\": ",
408                                            strerror(errno),
409                                            NULL);
410             (void)write(2, changer_resultstr, strlen(changer_resultstr));
411             exit(1);
412         }
413         if(arg) {
414             execle(tapechanger, tapechanger, cmd, arg, NULL, safe_env());
415         } else {
416             execle(tapechanger, tapechanger, cmd, NULL, safe_env());
417         }
418         changer_resultstr = vstralloc ("<error> ",
419                                        "could not exec \"",
420                                        tapechanger,
421                                        "\": ",
422                                        strerror(errno),
423                                        NULL);
424         (void)write(2, changer_resultstr, strlen(changer_resultstr));
425         exit(1);
426     default:
427         aclose(fd[1]);
428     }
429
430     if((changer_resultstr = areads(fd[0])) == NULL) {
431         changer_resultstr = vstralloc ("<error> ",
432                                        "could not read result from \"",
433                                        tapechanger,
434                                        errno ? "\": " : "\"",
435                                        errno ? strerror(errno) : "",
436                                        NULL);
437     }
438
439     while(1) {
440         if ((pid = wait(&wait_exitcode)) == -1) {
441             if(errno == EINTR) {
442                 continue;
443             } else {
444                 changer_resultstr = vstralloc ("<error> ",
445                                                "wait for \"",
446                                                tapechanger,
447                                                "\" failed: ",
448                                                strerror(errno),
449                                                NULL);
450                 exitcode = 2;
451                 goto done;
452             }
453         } else if (pid != changer_pid) {
454             ap_snprintf(num1, sizeof(num1), "%ld", (long)pid);
455             changer_resultstr = vstralloc ("<error> ",
456                                            "wait for \"",
457                                            tapechanger,
458                                            "\" returned unexpected pid ",
459                                            num1,
460                                            NULL);
461             exitcode = 2;
462             goto done;
463         } else {
464             break;
465         }
466     }
467
468     /* mark out-of-control changers as fatal error */
469     if(WIFSIGNALED(wait_exitcode)) {
470         ap_snprintf(num1, sizeof(num1), "%d", WTERMSIG(wait_exitcode));
471         changer_resultstr = newvstralloc (changer_resultstr,
472                                           "<error> ",
473                                           changer_resultstr,
474                                           " (got signal ", num1, ")",
475                                           NULL);
476         exitcode = 2;
477     } else {
478         exitcode = WEXITSTATUS(wait_exitcode);
479     }
480
481 done:
482     aclose(fd[0]);
483     aclose(fd[1]);
484
485 failed:
486     dbprintf(("changer: got exit: %d str: %s\n", exitcode, changer_resultstr)); 
487
488     amfree(cmdstr);
489
490     return exitcode;
491 }
492
493 /* This function commands the changerscript to look for a tape named
494    searchlabel. If is found, the changerscript answers with the device,
495    in which the tape can be accessed.
496 */
497 int changer_search(searchlabel, outslotstr, devicename)
498 char *searchlabel, **outslotstr, **devicename;
499 {
500     char *rest;
501     int rc;
502
503     dbprintf(("changer_search: %s\n",searchlabel));
504     rc = run_changer_command("-search", searchlabel, outslotstr, &rest);
505     if(rc) return rc;
506
507     if(*rest == '\0') return report_bad_resultstr();
508
509     *devicename = newstralloc(*devicename, rest);
510     return 0;
511 }
512
513 /* Because barcodelabel are short, and may not be the same as the 
514    amandalabels, the changerscript should be informed, which tapelabel
515    is associated with a tape. This function should be called after 
516    giving a label for a tape. (Maybe also, when the label and the associated
517    slot is known. e.g. during library scan.
518 */
519 int changer_label (slotsp,labelstr)
520 char *slotsp; 
521 char *labelstr;
522 {
523     int rc;
524     char *rest=NULL;
525     char *slotstr;
526     char *curslotstr = NULL;
527     int nslots, backwards, searchable;
528
529     dbprintf(("changer_label: %s for slot %s\n",labelstr,slotsp));
530     rc = changer_query(&nslots, &curslotstr, &backwards,&searchable);
531     amfree(curslotstr);
532
533     if ((rc == 0) && (searchable == 1)){
534         dbprintf(("changer_label: calling changer -label %s\n",labelstr));
535         rc = run_changer_command("-label", labelstr, &slotstr, &rest);
536         amfree(slotstr);
537     }
538
539     if(rc) return rc;
540
541     return 0;
542 }