ad9a83224f9c08ab3c357e1eeb1f6dd6a2675a91
[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.36 2006/08/24 01:57:16 paddy_s Exp $
28  *
29  * interface routines for tape changers
30  */
31 #include "amanda.h"
32 #include "util.h"
33 #include "conffile.h"
34 #include "version.h"
35
36 #include "changer.h"
37
38 /*
39  * If we don't have the new-style wait access functions, use our own,
40  * compatible with old-style BSD systems at least.  Note that we don't
41  * care about the case w_stopval == WSTOPPED since we don't ask to see
42  * stopped processes, so should never get them from wait.
43  */
44 #ifndef WEXITSTATUS
45 #   define WEXITSTATUS(r)       (((union wait *) &(r))->w_retcode)
46 #   define WTERMSIG(r)          (((union wait *) &(r))->w_termsig)
47
48 #   undef  WIFSIGNALED
49 #   define WIFSIGNALED(r)       (((union wait *) &(r))->w_termsig != 0)
50 #endif
51
52
53 int changer_debug = 0;
54 char *changer_resultstr = NULL;
55
56 static pid_t tpchanger_pid = -1;
57 static int tpchanger_stdout = -1;
58 static int tpchanger_stdin = -1;
59
60 /* local functions */
61 static int changer_command(char *cmd, char *arg);
62 static int report_bad_resultstr(char *cmd);
63 static int run_changer_command(char *cmd, char *arg, char **slotstr, char **rest);
64
65 int
66 changer_init(void)
67 {
68     return strcmp(getconf_str(CNF_TPCHANGER), "") != 0;
69 }
70
71
72 static int
73 report_bad_resultstr(char *cmd)
74 {
75     char *s;
76
77     s = vstrallocf(_("<error> badly formed result from changer command %s: \"%s\""),
78                   cmd, changer_resultstr);
79     amfree(changer_resultstr);
80     changer_resultstr = s;
81     return 2;
82 }
83
84 static int
85 run_changer_command(
86     char *      cmd,
87     char *      arg,
88     char **     slotstr,
89     char **     rest)
90 {
91     int exitcode;
92     char *result_copy;
93     char *slot;
94     char *s;
95     int ch;
96
97     if (slotstr) {
98         *slotstr = NULL;
99     }
100     if (rest) {
101         *rest = NULL;
102     }
103     exitcode = changer_command(cmd, arg);
104     s = changer_resultstr;
105     ch = *s++;
106
107     skip_whitespace(s, ch);
108     if(ch == '\0') return report_bad_resultstr(cmd);
109     slot = s - 1;
110     skip_non_whitespace(s, ch);
111     s[-1] = '\0';
112     if (slotstr) {
113         *slotstr = newstralloc(*slotstr, slot);
114     }
115     s[-1] = (char)ch;
116
117     skip_whitespace(s, ch);
118     if (rest) {
119         *rest = s - 1;
120     }
121
122     if(exitcode) {
123         if(ch == '\0') return report_bad_resultstr(cmd);
124         result_copy = stralloc(s - 1);
125         amfree(changer_resultstr);
126         changer_resultstr = result_copy;
127         return exitcode;
128     }
129     return 0;
130 }
131
132 int
133 changer_reset(
134     char **     slotstr)
135 {
136     char *rest;
137
138     return run_changer_command("-reset", (char *) NULL, slotstr, &rest);
139 }
140
141 int
142 changer_clean(
143     char **     slotstr)
144 {
145     char *rest;
146
147     return run_changer_command("-clean", (char *) NULL, slotstr, &rest);
148 }
149
150 int
151 changer_eject(
152     char **     slotstr)
153 {
154     char *rest;
155
156     return run_changer_command("-eject", (char *) NULL, slotstr, &rest);
157 }
158
159 int
160 changer_loadslot(
161     char *inslotstr,
162     char **outslotstr,
163     char **devicename)
164 {
165     char *rest;
166     int rc;
167
168     rc = run_changer_command("-slot", inslotstr, outslotstr, &rest);
169
170     if(rc) return rc;
171     if(*rest == '\0') return report_bad_resultstr("-slot");
172
173     *devicename = newstralloc(*devicename, rest);
174     return 0;
175 }
176
177
178 /*
179  * This function is somewhat equal to changer_info with one additional
180  * parameter, to get information, if the changer is able to search for
181  * tapelabels himself. E.g. Barcodereader
182  * The changer_script answers with an additional parameter, if it is able
183  * to search. This one should be 1, if it is able to search, and 0 if it
184  * knows about the extension. If the additional answer is omitted, the
185  * changer is not able to search for a tape. 
186  */
187
188 int
189 changer_query(
190     int *       nslotsp,
191     char **     curslotstr,
192     int *       backwardsp,
193     int *       searchable)
194 {
195     char *rest;
196     int rc;
197
198     rc = run_changer_command("-info", (char *) NULL, curslotstr, &rest);
199     if(rc) return rc;
200
201     dbprintf(_("changer_query: changer return was %s\n"),rest);
202     if (sscanf(rest, "%d %d %d", nslotsp, backwardsp, searchable) != 3) {
203       if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
204         return report_bad_resultstr("-info");
205       } else {
206         *searchable = 0;
207       }
208     }
209     dbprintf(_("changer_query: searchable = %d\n"),*searchable);
210     return 0;
211 }
212
213 int
214 changer_info(
215     int *       nslotsp,
216     char **     curslotstr,
217     int *       backwardsp)
218 {
219     char *rest;
220     int rc;
221
222     rc = run_changer_command("-info", (char *) NULL, curslotstr, &rest);
223     if(rc) return rc;
224
225     if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
226         return report_bad_resultstr("-info");
227     }
228     return 0;
229 }
230
231
232 /* ---------------------------- */
233
234 /*
235  * This function first uses searchlabel and changer_search, if
236  * the library is able to find a tape itself. If it is not, or if 
237  * the tape could not be found, then the normal scan is done.
238  *
239  * See interface documentation in changer.h.
240  */
241
242 void
243 changer_find(
244      void *     user_data,
245      int        (*user_init)(void *, int, int, int, int),
246      int        (*user_slot)(void *, int, char *, char *),
247      char *     searchlabel)
248 {
249     char *slotstr, *device = NULL, *curslotstr = NULL;
250     int nslots, checked, backwards, rc, done, searchable;
251
252     rc = changer_query(&nslots, &curslotstr, &backwards, &searchable);
253
254     if (rc != 0) {
255         /* Problem with the changer script. Bail. */
256         g_fprintf(stderr, _("Changer problem: %s\n"), changer_resultstr);
257         amfree(curslotstr);
258         return;
259     }
260
261     done = user_init(user_data, rc, nslots, backwards, searchable);
262     amfree(curslotstr);
263    
264     if (searchlabel != NULL)
265     {
266       dbprintf(_("changer_find: looking for %s changer is searchable = %d\n"),
267                 searchlabel, searchable);
268     } else {
269       dbprintf(_("changer_find: looking for NULL changer is searchable = %d\n"),
270                 searchable);
271     }
272
273     if ((searchlabel!=NULL) && searchable && !done){
274       rc=changer_search(searchlabel, &curslotstr, &device);
275       if(rc == 0)
276         done = user_slot(user_data, rc, curslotstr, device);
277     }
278  
279     slotstr = "current";
280     checked = 0;
281
282     while(!done && checked < nslots) {
283         rc = changer_loadslot(slotstr, &curslotstr, &device);
284         if(rc > 0)
285             done = user_slot(user_data, rc, curslotstr, device);
286         else if(!done)
287             done = user_slot(user_data, 0,  curslotstr, device);
288         amfree(curslotstr);
289         amfree(device);
290
291         checked += 1;
292         slotstr = "next";
293     }
294 }
295
296 /* ---------------------------- */
297
298 void
299 changer_current(
300     void *      user_data,
301     int         (*user_init)(void *, int, int, int, int),
302     int         (*user_slot)(void *, int, char *, char *))
303 {
304     char *device = NULL, *curslotstr = NULL;
305     int nslots, backwards, rc, done, searchable;
306
307     rc = changer_query(&nslots, &curslotstr, &backwards, &searchable);
308     done = user_init(user_data, rc, nslots, backwards, searchable);
309     amfree(curslotstr);
310
311     rc = changer_loadslot("current", &curslotstr, &device);
312     if(rc > 0) {
313         done = user_slot(user_data, rc, curslotstr, device);
314     } else if(!done) {
315         done = user_slot(user_data, 0,  curslotstr, device);
316     }
317     amfree(curslotstr);
318     amfree(device);
319 }
320
321 /* ---------------------------- */
322
323 static int
324 start_chg_glue(void)
325 {
326     int stdin_pipe[2] = { -1, -1 };
327     int stdout_pipe[2] = { -1, -1 };
328     char *chg_glue;
329
330     /* is it already running? */
331     if (tpchanger_pid != -1)
332         return 1;
333
334     if (pipe(stdin_pipe) == -1 || pipe(stdout_pipe) == -1) {
335         changer_resultstr = vstrallocf(
336                         _("<error> could not make pipe: %s"), strerror(errno));
337         goto error;
338     }
339
340     switch(tpchanger_pid = fork()) {
341     case -1:
342         changer_resultstr = vstrallocf(
343                         _("<error> could not fork: %s"), strerror(errno));
344         goto error;
345
346     case 0:
347         debug_dup_stderr_to_debug();
348         if(dup2(stdin_pipe[0], 0) == -1) {
349             changer_resultstr = vstrallocf(
350                         _("<error> could not dup2: %s"), strerror(errno));
351             goto child_err;
352         }
353
354         if(dup2(stdout_pipe[1], 1) == -1) {
355             changer_resultstr = vstrallocf(
356                         _("<error> could not dup2: %s"), strerror(errno));
357             goto child_err;
358         }
359         safe_fd(-1, 0);
360
361         chg_glue = g_strdup_printf("%s/chg-glue", amlibexecdir);
362
363         execl(chg_glue, chg_glue, get_config_name(), NULL);
364         changer_resultstr = vstrallocf(
365                         _("<error> could not exec \"chg-glue\": %s"), strerror(errno));
366         goto child_err;
367
368 child_err:
369         (void)full_write(stdout_pipe[1], changer_resultstr, strlen(changer_resultstr));
370         exit(1);
371
372     default:
373         aclose(stdin_pipe[0]);
374         aclose(stdout_pipe[1]);
375
376         tpchanger_stdout = stdout_pipe[0];
377         tpchanger_stdin = stdin_pipe[1];
378
379         return 1;
380     }
381
382 error:
383     aclose(stdin_pipe[0]);
384     aclose(stdin_pipe[1]);
385     aclose(stdout_pipe[0]);
386     aclose(stdout_pipe[1]);
387
388     return 0;
389 }
390
391 static int
392 changer_command(
393      char *cmd,
394      char *arg)
395 {
396     int exitcode = 0;
397     char *cmdstr = NULL;
398
399     amfree(changer_resultstr);
400
401     if (!start_chg_glue()) {
402         exitcode = 2;
403         goto failed;
404     }
405
406     cmdstr = vstralloc(cmd,
407                        arg ? " " : "",
408                        arg ? arg : "",
409                        "\n",
410                        NULL);
411
412     g_debug("changer: >> %s %s", cmd, arg? arg : "");
413
414     /* write the command to chg_glue */
415     if (full_write(tpchanger_stdin, cmdstr, strlen(cmdstr)) != strlen(cmdstr)) {
416         changer_resultstr = g_strdup("<error> chg-glue exited unexpectedly");
417         exitcode = 2;
418         goto failed;
419     }
420
421     /* read the first line of the response */
422     changer_resultstr = areads(tpchanger_stdout);
423     if (!changer_resultstr || !*changer_resultstr) {
424         changer_resultstr = g_strdup("<error> unexpected EOF");
425         exitcode = 2;
426         goto failed;
427     }
428     g_debug("changer: << %s", changer_resultstr);
429
430     if (strncmp_const(changer_resultstr, "EXITSTATUS ") != 0) {
431         report_bad_resultstr(cmd);
432         exitcode = 2;
433         goto failed;
434     }
435     exitcode = atoi(changer_resultstr + strlen("EXITSTATUS "));
436
437     /* and the second */
438     changer_resultstr = areads(tpchanger_stdout);
439     if (!changer_resultstr) {
440         changer_resultstr = g_strdup("<error> unexpected EOF");
441         exitcode = 2;
442         goto failed;
443     }
444     g_debug("changer: << %s", changer_resultstr);
445
446 failed:
447     if (exitcode != 0) {
448         g_debug("changer: ERROR %s", changer_resultstr);
449     }
450
451     amfree(cmdstr);
452
453     return exitcode;
454 }
455
456
457 /*
458  * This function commands the changerscript to look for a tape named
459  * searchlabel. If is found, the changerscript answers with the device,
460  * in which the tape can be accessed.
461  */
462
463 int
464 changer_search(
465     char *      searchlabel,
466     char **     outslotstr,
467     char **     devicename)
468 {
469     char *rest;
470     int rc;
471
472     dbprintf("changer_search: %s\n",searchlabel);
473     rc = run_changer_command("-search", searchlabel, outslotstr, &rest);
474     if(rc) return rc;
475
476     if(*rest == '\0') return report_bad_resultstr("-search");
477
478     *devicename = newstralloc(*devicename, rest);
479     return 0;
480 }
481
482
483 /*
484  * Because barcodelabel are short, and may not be the same as the 
485  * amandalabels, the changerscript should be informed, which tapelabel
486  * is associated with a tape. This function should be called after 
487  * giving a label for a tape. (Maybe also, when the label and the associated
488  * slot is known. e.g. during library scan.
489  */
490
491 int
492 changer_label(
493     char *      slotsp, 
494     char *      labelstr)
495 {
496     int rc;
497     char *rest=NULL;
498     char *slotstr;
499     char *curslotstr = NULL;
500     int nslots, backwards, searchable;
501
502     dbprintf(_("changer_label: %s for slot %s\n"),labelstr,slotsp);
503     rc = changer_query(&nslots, &curslotstr, &backwards,&searchable);
504     amfree(curslotstr);
505
506     if ((rc == 0) && (searchable == 1)){
507         dbprintf(_("changer_label: calling changer -label %s\n"),labelstr);
508         rc = run_changer_command("-label", labelstr, &slotstr, &rest);
509         amfree(slotstr);
510     }
511
512     if(rc) return rc;
513
514     return 0;
515 }