Imported Upstream version 2.6.1
[debian/amanda] / server-src / changer.c
index c4e2935a0a69a4942b991e00d9ea8a52356230d4..ad9a83224f9c08ab3c357e1eeb1f6dd6a2675a91 100644 (file)
@@ -24,7 +24,7 @@
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: changer.c,v 1.29 2006/01/14 04:37:19 paddy_s Exp $
+ * $Id: changer.c,v 1.36 2006/08/24 01:57:16 paddy_s Exp $
  *
  * interface routines for tape changers
  */
 int changer_debug = 0;
 char *changer_resultstr = NULL;
 
-static char *tapechanger = NULL;
+static pid_t tpchanger_pid = -1;
+static int tpchanger_stdout = -1;
+static int tpchanger_stdin = -1;
 
 /* local functions */
-static int changer_command P((char *cmd, char *arg));
+static int changer_command(char *cmd, char *arg);
+static int report_bad_resultstr(char *cmd);
+static int run_changer_command(char *cmd, char *arg, char **slotstr, char **rest);
 
-int changer_init()
+int
+changer_init(void)
 {
-    tapechanger = getconf_str(CNF_TPCHANGER);
-    return strcmp(tapechanger, "") != 0;
+    return strcmp(getconf_str(CNF_TPCHANGER), "") != 0;
 }
 
 
-static int report_bad_resultstr()
+static int
+report_bad_resultstr(char *cmd)
 {
     char *s;
 
-    s = vstralloc("badly formed result from changer: ",
-                 "\"", changer_resultstr, "\"",
-                 NULL);
+    s = vstrallocf(_("<error> badly formed result from changer command %s: \"%s\""),
+                 cmd, changer_resultstr);
     amfree(changer_resultstr);
     changer_resultstr = s;
     return 2;
 }
 
-static int run_changer_command(cmd, arg, slotstr, rest)
-char *cmd;
-char *arg;
-char **slotstr;
-char **rest;
+static int
+run_changer_command(
+    char *     cmd,
+    char *     arg,
+    char **    slotstr,
+    char **    rest)
 {
     int exitcode;
     char *result_copy;
@@ -89,25 +94,33 @@ char **rest;
     char *s;
     int ch;
 
-    *slotstr = NULL;
-    *rest = NULL;
+    if (slotstr) {
+        *slotstr = NULL;
+    }
+    if (rest) {
+        *rest = NULL;
+    }
     exitcode = changer_command(cmd, arg);
     s = changer_resultstr;
     ch = *s++;
 
     skip_whitespace(s, ch);
-    if(ch == '\0') return report_bad_resultstr();
+    if(ch == '\0') return report_bad_resultstr(cmd);
     slot = s - 1;
     skip_non_whitespace(s, ch);
     s[-1] = '\0';
-    *slotstr = newstralloc(*slotstr, slot);
-    s[-1] = ch;
+    if (slotstr) {
+        *slotstr = newstralloc(*slotstr, slot);
+    }
+    s[-1] = (char)ch;
 
     skip_whitespace(s, ch);
-    *rest = s - 1;
+    if (rest) {
+        *rest = s - 1;
+    }
 
     if(exitcode) {
-       if(ch == '\0') return report_bad_resultstr();
+       if(ch == '\0') return report_bad_resultstr(cmd);
        result_copy = stralloc(s - 1);
        amfree(changer_resultstr);
        changer_resultstr = result_copy;
@@ -116,32 +129,38 @@ char **rest;
     return 0;
 }
 
-int changer_reset(slotstr)
-char **slotstr;
+int
+changer_reset(
+    char **    slotstr)
 {
     char *rest;
 
     return run_changer_command("-reset", (char *) NULL, slotstr, &rest);
 }
 
-int changer_clean(slotstr)
-char **slotstr;
+int
+changer_clean(
+    char **    slotstr)
 {
     char *rest;
 
     return run_changer_command("-clean", (char *) NULL, slotstr, &rest);
 }
 
-int changer_eject(slotstr)
-char **slotstr;
+int
+changer_eject(
+    char **    slotstr)
 {
     char *rest;
 
     return run_changer_command("-eject", (char *) NULL, slotstr, &rest);
 }
 
-int changer_loadslot(inslotstr, outslotstr, devicename)
-char *inslotstr, **outslotstr, **devicename;
+int
+changer_loadslot(
+    char *inslotstr,
+    char **outslotstr,
+    char **devicename)
 {
     char *rest;
     int rc;
@@ -149,23 +168,29 @@ char *inslotstr, **outslotstr, **devicename;
     rc = run_changer_command("-slot", inslotstr, outslotstr, &rest);
 
     if(rc) return rc;
-    if(*rest == '\0') return report_bad_resultstr();
+    if(*rest == '\0') return report_bad_resultstr("-slot");
 
     *devicename = newstralloc(*devicename, rest);
     return 0;
 }
 
-/* This function is somewhat equal to changer_info with one additional
-   parameter, to get information, if the changer is able to search for
-   tapelabels himself. E.g. Barcodereader
-   The changer_script answers with an additional parameter, if it is able
-   to search. This one should be 1, if it is able to search, and 0 if it
-   knows about the extension. If the additional answer is omitted, the
-   changer is not able to search for a tape. 
-*/
-int changer_query(nslotsp, curslotstr, backwardsp, searchable)
-int *nslotsp, *backwardsp, *searchable;
-char **curslotstr;
+
+/*
+ * This function is somewhat equal to changer_info with one additional
+ * parameter, to get information, if the changer is able to search for
+ * tapelabels himself. E.g. Barcodereader
+ * The changer_script answers with an additional parameter, if it is able
+ * to search. This one should be 1, if it is able to search, and 0 if it
+ * knows about the extension. If the additional answer is omitted, the
+ * changer is not able to search for a tape. 
+ */
+
+int
+changer_query(
+    int *      nslotsp,
+    char **    curslotstr,
+    int *      backwardsp,
+    int *      searchable)
 {
     char *rest;
     int rc;
@@ -173,21 +198,23 @@ char **curslotstr;
     rc = run_changer_command("-info", (char *) NULL, curslotstr, &rest);
     if(rc) return rc;
 
-    dbprintf(("changer_query: changer return was %s\n",rest));
+    dbprintf(_("changer_query: changer return was %s\n"),rest);
     if (sscanf(rest, "%d %d %d", nslotsp, backwardsp, searchable) != 3) {
       if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
-        return report_bad_resultstr();
+        return report_bad_resultstr("-info");
       } else {
         *searchable = 0;
       }
     }
-    dbprintf(("changer_query: searchable = %d\n",*searchable));
+    dbprintf(_("changer_query: searchable = %d\n"),*searchable);
     return 0;
 }
 
-int changer_info(nslotsp, curslotstr, backwardsp)
-int *nslotsp, *backwardsp;
-char **curslotstr;
+int
+changer_info(
+    int *      nslotsp,
+    char **    curslotstr,
+    int *      backwardsp)
 {
     char *rest;
     int rc;
@@ -196,7 +223,7 @@ char **curslotstr;
     if(rc) return rc;
 
     if (sscanf(rest, "%d %d", nslotsp, backwardsp) != 2) {
-       return report_bad_resultstr();
+       return report_bad_resultstr("-info");
     }
     return 0;
 }
@@ -204,40 +231,49 @@ char **curslotstr;
 
 /* ---------------------------- */
 
-/* This function first uses searchlabel and changer_search, if
-   the library is able to find a tape itself. If it is not, or if 
-   the tape could not be found, then the normal scan is done.
-   See interface documentation in changer.h.
-*/
-void changer_find(user_data, user_init, user_slot, searchlabel)
-     void *user_data;
-     int (*user_init) P((void *user_data, int rc, int nslots, int backwards,
-                         int searchable));
-     int (*user_slot) P((void *user_data, int rc, char *slotstr,
-                         char *device));
-     char *searchlabel;
+/*
+ * This function first uses searchlabel and changer_search, if
+ * the library is able to find a tape itself. If it is not, or if 
+ * the tape could not be found, then the normal scan is done.
+ *
+ * See interface documentation in changer.h.
+ */
+
+void
+changer_find(
+     void *    user_data,
+     int       (*user_init)(void *, int, int, int, int),
+     int       (*user_slot)(void *, int, char *, char *),
+     char *    searchlabel)
 {
     char *slotstr, *device = NULL, *curslotstr = NULL;
     int nslots, checked, backwards, rc, done, searchable;
 
     rc = changer_query(&nslots, &curslotstr, &backwards, &searchable);
+
+    if (rc != 0) {
+        /* Problem with the changer script. Bail. */
+        g_fprintf(stderr, _("Changer problem: %s\n"), changer_resultstr);
+        amfree(curslotstr);
+        return;
+    }
+
     done = user_init(user_data, rc, nslots, backwards, searchable);
     amfree(curslotstr);
    
     if (searchlabel != NULL)
     {
-      dbprintf(("changer_find: looking for %s changer is searchable = %d\n",
-               searchlabel, searchable));
+      dbprintf(_("changer_find: looking for %s changer is searchable = %d\n"),
+               searchlabel, searchable);
     } else {
-      dbprintf(("changer_find: looking for NULL changer is searchable = %d\n",
-               searchable));
+      dbprintf(_("changer_find: looking for NULL changer is searchable = %d\n"),
+               searchable);
     }
 
     if ((searchlabel!=NULL) && searchable && !done){
-      rc=changer_search(searchlabel,&curslotstr,&device);
+      rc=changer_search(searchlabel, &curslotstr, &device);
       if(rc == 0)
-        done = user_slot(user_data, rc,curslotstr,device);
+        done = user_slot(user_data, rc, curslotstr, device);
     }
  
     slotstr = "current";
@@ -259,10 +295,11 @@ void changer_find(user_data, user_init, user_slot, searchlabel)
 
 /* ---------------------------- */
 
-void changer_current(user_data, user_init, user_slot)
-     void *user_data;
-int (*user_init) P((void *ud, int rc, int nslots, int backwards, int searchable));
-int (*user_slot) P((void *ud, int rc, char *slotstr, char *device));
+void
+changer_current(
+    void *     user_data,
+    int                (*user_init)(void *, int, int, int, int),
+    int                (*user_slot)(void *, int, char *, char *))
 {
     char *device = NULL, *curslotstr = NULL;
     int nslots, backwards, rc, done, searchable;
@@ -283,220 +320,178 @@ int (*user_slot) P((void *ud, int rc, char *slotstr, char *device));
 
 /* ---------------------------- */
 
-static int changer_command(cmd, arg)
-     char *cmd;
-     char *arg;
+static int
+start_chg_glue(void)
 {
-    int fd[2];
-    amwait_t wait_exitcode;
-    int exitcode;
-    char num1[NUM_STR_SIZE];
-    char num2[NUM_STR_SIZE];
-    char *cmdstr;
-    pid_t pid, changer_pid = 0;
-
-    if (*tapechanger != '/') {
-       tapechanger = vstralloc(libexecdir, "/", tapechanger, versionsuffix(),
-                               NULL);
-       malloc_mark(tapechanger);
+    int stdin_pipe[2] = { -1, -1 };
+    int stdout_pipe[2] = { -1, -1 };
+    char *chg_glue;
+
+    /* is it already running? */
+    if (tpchanger_pid != -1)
+       return 1;
+
+    if (pipe(stdin_pipe) == -1 || pipe(stdout_pipe) == -1) {
+       changer_resultstr = vstrallocf(
+                       _("<error> could not make pipe: %s"), strerror(errno));
+       goto error;
     }
-    cmdstr = vstralloc(tapechanger, " ",
-                      cmd, arg ? " " : "", 
-                      arg ? arg : "",
-                      NULL);
 
-    if(changer_debug) {
-       fprintf(stderr, "changer: opening pipe to: %s\n", cmdstr);
-       fflush(stderr);
+    switch(tpchanger_pid = fork()) {
+    case -1:
+       changer_resultstr = vstrallocf(
+                       _("<error> could not fork: %s"), strerror(errno));
+       goto error;
+
+    case 0:
+       debug_dup_stderr_to_debug();
+       if(dup2(stdin_pipe[0], 0) == -1) {
+           changer_resultstr = vstrallocf(
+                       _("<error> could not dup2: %s"), strerror(errno));
+           goto child_err;
+       }
+
+       if(dup2(stdout_pipe[1], 1) == -1) {
+           changer_resultstr = vstrallocf(
+                       _("<error> could not dup2: %s"), strerror(errno));
+           goto child_err;
+       }
+       safe_fd(-1, 0);
+
+       chg_glue = g_strdup_printf("%s/chg-glue", amlibexecdir);
+
+       execl(chg_glue, chg_glue, get_config_name(), NULL);
+       changer_resultstr = vstrallocf(
+                       _("<error> could not exec \"chg-glue\": %s"), strerror(errno));
+       goto child_err;
+
+child_err:
+       (void)full_write(stdout_pipe[1], changer_resultstr, strlen(changer_resultstr));
+       exit(1);
+
+    default:
+       aclose(stdin_pipe[0]);
+       aclose(stdout_pipe[1]);
+
+       tpchanger_stdout = stdout_pipe[0];
+       tpchanger_stdin = stdin_pipe[1];
+
+       return 1;
     }
 
+error:
+    aclose(stdin_pipe[0]);
+    aclose(stdin_pipe[1]);
+    aclose(stdout_pipe[0]);
+    aclose(stdout_pipe[1]);
+
+    return 0;
+}
+
+static int
+changer_command(
+     char *cmd,
+     char *arg)
+{
+    int exitcode = 0;
+    char *cmdstr = NULL;
+
     amfree(changer_resultstr);
 
-    if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not create pipe for \"",
-                                      cmdstr,
-                                      "\": ",
-                                      strerror(errno),
-                                      NULL);
+    if (!start_chg_glue()) {
        exitcode = 2;
        goto failed;
     }
-    if(fd[0] < 0 || fd[0] >= FD_SETSIZE) {
-       snprintf(num1, sizeof(num1), "%d", fd[0]);
-       snprintf(num2, sizeof(num2), "%d", FD_SETSIZE-1);
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not create pipe for \"",
-                                      cmdstr,
-                                      "\": ",
-                                      "socketpair 0: descriptor ",
-                                      num1,
-                                      " out of range ( .. ",
-                                      num2,
-                                      ")",
-                                      NULL);
-       exitcode = 2;
-       goto done;
-    }
-    if(fd[1] < 0 || fd[1] >= FD_SETSIZE) {
-       snprintf(num1, sizeof(num1), "%d", fd[1]);
-       snprintf(num2, sizeof(num2), "%d", FD_SETSIZE-1);
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not create pipe for \"",
-                                      cmdstr,
-                                      "\": ",
-                                      "socketpair 1: descriptor ",
-                                      num1,
-                                      " out of range ( .. ",
-                                      num2,
-                                      ")",
-                                      NULL);
-       exitcode = 2;
-       goto done;
-    }
 
-    switch(changer_pid = fork()) {
-    case -1:
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not fork for \"",
-                                      cmdstr,
-                                      "\": ",
-                                      strerror(errno),
-                                      NULL);
-       exitcode = 2;
-       goto done;
-    case 0:
-       if(dup2(fd[1], 1) == -1 || dup2(fd[1], 2) == -1) {
-           changer_resultstr = vstralloc ("<error> ",
-                                          "could not open pipe to \"",
-                                          cmdstr,
-                                          "\": ",
-                                          strerror(errno),
-                                          NULL);
-           (void)fullwrite(fd[1], changer_resultstr, strlen(changer_resultstr));
-           exit(1);
-       }
-       aclose(fd[0]);
-       aclose(fd[1]);
-       if(config_dir && chdir(config_dir) == -1) {
-           changer_resultstr = vstralloc ("<error> ",
-                                          "could not cd to \"",
-                                          config_dir,
-                                          "\": ",
-                                          strerror(errno),
-                                          NULL);
-           (void)fullwrite(2, changer_resultstr, strlen(changer_resultstr));
-           exit(1);
-       }
-       if(arg) {
-           execle(tapechanger, tapechanger, cmd, arg, NULL, safe_env());
-       } else {
-           execle(tapechanger, tapechanger, cmd, NULL, safe_env());
-       }
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not exec \"",
-                                      tapechanger,
-                                      "\": ",
-                                      strerror(errno),
-                                      NULL);
-       (void)fullwrite(2, changer_resultstr, strlen(changer_resultstr));
-       exit(1);
-    default:
-       aclose(fd[1]);
-    }
+    cmdstr = vstralloc(cmd,
+                      arg ? " " : "",
+                      arg ? arg : "",
+                      "\n",
+                      NULL);
 
-    if((changer_resultstr = areads(fd[0])) == NULL) {
-       changer_resultstr = vstralloc ("<error> ",
-                                      "could not read result from \"",
-                                      tapechanger,
-                                      errno ? "\": " : "\"",
-                                      errno ? strerror(errno) : "",
-                                      NULL);
+    g_debug("changer: >> %s %s", cmd, arg? arg : "");
+
+    /* write the command to chg_glue */
+    if (full_write(tpchanger_stdin, cmdstr, strlen(cmdstr)) != strlen(cmdstr)) {
+        changer_resultstr = g_strdup("<error> chg-glue exited unexpectedly");
+       exitcode = 2;
+       goto failed;
     }
 
-    while(1) {
-       if ((pid = wait(&wait_exitcode)) == -1) {
-           if(errno == EINTR) {
-               continue;
-           } else {
-               changer_resultstr = vstralloc ("<error> ",
-                                              "wait for \"",
-                                              tapechanger,
-                                              "\" failed: ",
-                                              strerror(errno),
-                                              NULL);
-               exitcode = 2;
-               goto done;
-           }
-       } else if (pid != changer_pid) {
-           snprintf(num1, sizeof(num1), "%ld", (long)pid);
-           changer_resultstr = vstralloc ("<error> ",
-                                          "wait for \"",
-                                          tapechanger,
-                                          "\" returned unexpected pid ",
-                                          num1,
-                                          NULL);
-           exitcode = 2;
-           goto done;
-       } else {
-           break;
-       }
+    /* read the first line of the response */
+    changer_resultstr = areads(tpchanger_stdout);
+    if (!changer_resultstr || !*changer_resultstr) {
+        changer_resultstr = g_strdup("<error> unexpected EOF");
+        exitcode = 2;
+        goto failed;
     }
+    g_debug("changer: << %s", changer_resultstr);
 
-    /* mark out-of-control changers as fatal error */
-    if(WIFSIGNALED(wait_exitcode)) {
-       snprintf(num1, sizeof(num1), "%d", WTERMSIG(wait_exitcode));
-       changer_resultstr = newvstralloc (changer_resultstr,
-                                         "<error> ",
-                                         changer_resultstr,
-                                         " (got signal ", num1, ")",
-                                         NULL);
+    if (strncmp_const(changer_resultstr, "EXITSTATUS ") != 0) {
+       report_bad_resultstr(cmd);
        exitcode = 2;
-    } else {
-       exitcode = WEXITSTATUS(wait_exitcode);
+       goto failed;
     }
-
-done:
-    aclose(fd[0]);
-    aclose(fd[1]);
+    exitcode = atoi(changer_resultstr + strlen("EXITSTATUS "));
+
+    /* and the second */
+    changer_resultstr = areads(tpchanger_stdout);
+    if (!changer_resultstr) {
+        changer_resultstr = g_strdup("<error> unexpected EOF");
+        exitcode = 2;
+        goto failed;
+    }
+    g_debug("changer: << %s", changer_resultstr);
 
 failed:
-    dbprintf(("changer: got exit: %d str: %s\n", exitcode, changer_resultstr)); 
+    if (exitcode != 0) {
+       g_debug("changer: ERROR %s", changer_resultstr);
+    }
 
     amfree(cmdstr);
 
     return exitcode;
 }
 
-/* This function commands the changerscript to look for a tape named
-   searchlabel. If is found, the changerscript answers with the device,
-   in which the tape can be accessed.
-*/
-int changer_search(searchlabel, outslotstr, devicename)
-char *searchlabel, **outslotstr, **devicename;
+
+/*
+ * This function commands the changerscript to look for a tape named
+ * searchlabel. If is found, the changerscript answers with the device,
+ * in which the tape can be accessed.
+ */
+
+int
+changer_search(
+    char *     searchlabel,
+    char **    outslotstr,
+    char **    devicename)
 {
     char *rest;
     int rc;
 
-    dbprintf(("changer_search: %s\n",searchlabel));
+    dbprintf("changer_search: %s\n",searchlabel);
     rc = run_changer_command("-search", searchlabel, outslotstr, &rest);
     if(rc) return rc;
 
-    if(*rest == '\0') return report_bad_resultstr();
+    if(*rest == '\0') return report_bad_resultstr("-search");
 
     *devicename = newstralloc(*devicename, rest);
     return 0;
 }
 
-/* Because barcodelabel are short, and may not be the same as the 
-   amandalabels, the changerscript should be informed, which tapelabel
-   is associated with a tape. This function should be called after 
-   giving a label for a tape. (Maybe also, when the label and the associated
-   slot is known. e.g. during library scan.
-*/
-int changer_label (slotsp,labelstr)
-char *slotsp; 
-char *labelstr;
+
+/*
+ * Because barcodelabel are short, and may not be the same as the 
+ * amandalabels, the changerscript should be informed, which tapelabel
+ * is associated with a tape. This function should be called after 
+ * giving a label for a tape. (Maybe also, when the label and the associated
+ * slot is known. e.g. during library scan.
+ */
+
+int
+changer_label(
+    char *     slotsp, 
+    char *     labelstr)
 {
     int rc;
     char *rest=NULL;
@@ -504,12 +499,12 @@ char *labelstr;
     char *curslotstr = NULL;
     int nslots, backwards, searchable;
 
-    dbprintf(("changer_label: %s for slot %s\n",labelstr,slotsp));
+    dbprintf(_("changer_label: %s for slot %s\n"),labelstr,slotsp);
     rc = changer_query(&nslots, &curslotstr, &backwards,&searchable);
     amfree(curslotstr);
 
     if ((rc == 0) && (searchable == 1)){
-       dbprintf(("changer_label: calling changer -label %s\n",labelstr));
+       dbprintf(_("changer_label: calling changer -label %s\n"),labelstr);
        rc = run_changer_command("-label", labelstr, &slotstr, &rest);
        amfree(slotstr);
     }