Merge branch 'master' into squeeze
[debian/amanda] / ndmp-src / ndma_ctrl_robot.c
diff --git a/ndmp-src/ndma_ctrl_robot.c b/ndmp-src/ndma_ctrl_robot.c
new file mode 100644 (file)
index 0000000..0face0f
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 1998,1999,2000
+ *     Traakan, Inc., Los Altos, CA
+ *     All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Project:  NDMJOB
+ * Ident:    $Id: $
+ *
+ * Description:
+ *
+ */
+
+
+#include "ndmagents.h"
+
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+
+
+int
+ndmca_robot_issue_scsi_req (struct smc_ctrl_block *smc)
+{
+       struct ndmconn *        conn = (struct ndmconn *) smc->app_data;
+       struct smc_scsi_req *   sr = &smc->scsi_req;
+       int                     rc;
+
+       rc = ndmscsi_execute (conn, (struct ndmscsi_request *) sr, 0);
+       return rc;
+}
+
+
+int
+ndmca_robot_prep_target (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       int                     rc;
+
+       NDMOS_MACRO_ZEROFILL (smc);
+
+       smc->app_data = sess->plumb.robot;
+       smc->issue_scsi_req = ndmca_robot_issue_scsi_req;
+
+       rc = ndmscsi_use (sess->plumb.robot,
+                               &sess->control_acb.job.robot_target);
+       if (rc) return rc;
+
+       return 0;
+}
+
+int
+ndmca_robot_obtain_info (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       int                     rc;
+
+       rc = smc_inquire (smc);
+       if (rc) return rc;
+
+       rc = smc_get_elem_aa (smc);
+       if (rc) return rc;
+
+       rc = smc_read_elem_status (smc);
+       if (rc) return rc;
+
+       return 0;
+}
+
+int
+ndmca_robot_init_elem_status (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       int                     rc;
+
+       ndmalogf (sess, 0, 1,
+           "Commanding robot to initialize element status (take inventory)");
+
+       rc = smc_init_elem_status (smc);
+       if (rc) {
+               ndmalogf (sess, 0, 0, "init-elem-status failed");
+               return rc;
+       }
+
+       return 0;
+}
+
+int
+ndmca_robot_startup (struct ndm_session *sess)
+{
+       int             rc;
+
+       if (!sess->control_acb.job.have_robot)
+               return -1;      /* Huh? why were we called */
+
+       rc = ndmca_connect_robot_agent(sess);
+       if (rc) return rc;
+
+       rc = ndmca_robot_prep_target(sess);
+       if (rc) return rc;
+
+       return 0;
+}
+
+int
+ndmca_robot_move (struct ndm_session *sess, int src_addr, int dst_addr)
+{
+       struct ndm_control_agent *ca = &sess->control_acb;
+       struct smc_ctrl_block * smc = &ca->smc_cb;
+       int                     rc;
+       unsigned int            t;
+
+       ndmalogf (sess, 0, 2, "robot moving @%d to @%d",
+                       src_addr, dst_addr);
+
+       rc = -1;
+       for (t = 0; t <= ca->job.robot_timeout; t += 10) {
+               if (t > 0) {
+                       ndmalogf (sess, 0, 2,
+                               "Pausing ten seconds before retry (%d/%d)",
+                               t, ca->job.robot_timeout);
+                       sleep (10);
+               }
+               rc = smc_move (smc, src_addr, dst_addr,
+                                       0, smc->elem_aa.mte_addr);
+               if (rc == 0) break;
+       }
+
+       if (rc == 0) {
+               ndmalogf (sess, 0, 2, "robot move OK @%d to @%d",
+                               src_addr, dst_addr);
+       } else {
+               ndmalogf (sess, 0, 2, "robot move BAD @%d to @%d",
+                       src_addr, dst_addr);
+       }
+
+       return rc;
+}
+
+int
+ndmca_robot_load (struct ndm_session *sess, int slot_addr)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       unsigned                dte_addr = smc->elem_aa.dte_addr;
+       int                     rc;
+
+       if (sess->control_acb.job.drive_addr_given)
+               dte_addr = sess->control_acb.job.drive_addr;
+
+       ndmalogf (sess, 0, 1,
+                       "Commanding robot to load slot @%d into drive @%d",
+                       slot_addr, dte_addr);
+
+       rc = ndmca_robot_move (sess, slot_addr, dte_addr);
+
+       return rc;
+}
+
+int
+ndmca_robot_unload (struct ndm_session *sess, int slot_addr)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       int                     dte_addr = smc->elem_aa.dte_addr;
+       int                     rc;
+
+       if (sess->control_acb.job.drive_addr_given)
+               dte_addr = sess->control_acb.job.drive_addr;
+
+       /* tricky part -- some (most?) robots need the drive to eject */
+
+       ndmalogf (sess, 0, 1,
+                       "Commanding robot to unload drive @%d to slot @%d",
+                       dte_addr, slot_addr);
+
+       rc = ndmca_robot_move (sess, dte_addr, slot_addr);
+
+       return rc;
+}
+
+
+struct smc_element_descriptor *
+ndmca_robot_find_element (struct ndm_session *sess, int element_address)
+{
+       struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
+       unsigned int                    i;
+       struct smc_element_descriptor * edp;
+
+       for (i = 0; i < smc->n_elem_desc; i++) {
+               edp = &smc->elem_desc[i];
+               if (edp->element_address == element_address)
+                       return edp;
+       }
+
+       return 0;
+}
+
+int
+ndmca_robot_check_ready (struct ndm_session *sess)
+{
+       struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
+       unsigned                        first_dte_addr;
+       unsigned                        n_dte_addr;
+       int                             rc;
+       unsigned int                    i;
+       int                             errcnt = 0;
+       struct smc_element_descriptor * edp;
+
+       rc = ndmca_robot_obtain_info (sess);
+       if (rc) return rc;
+
+       if (sess->control_acb.job.remedy_all) {
+               first_dte_addr = smc->elem_aa.dte_addr;
+               n_dte_addr = smc->elem_aa.dte_count;
+       } else {
+               n_dte_addr = 1;
+               if (sess->control_acb.job.drive_addr_given) {
+                       first_dte_addr = sess->control_acb.job.drive_addr;
+               } else {
+                       first_dte_addr = smc->elem_aa.dte_addr;
+               }
+       }
+
+       for (i = 0; i < n_dte_addr; i++) {
+               edp = ndmca_robot_find_element (sess, first_dte_addr+i);
+
+               if (!edp->Full)
+                       continue;
+
+               ndmalogf (sess, 0, 1, "tape drive @%d not empty",
+                               edp->element_address);
+               errcnt++;
+       }
+
+       return errcnt;
+}
+
+int
+ndmca_robot_remedy_ready (struct ndm_session *sess)
+{
+       struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
+       int                             rc;
+       unsigned int                    i;
+       int                             errcnt;
+       struct smc_element_descriptor * edp;
+       struct smc_element_descriptor * edp2;
+       unsigned                        first_dte_addr;
+       unsigned                        n_dte_addr;
+       char                            prefix[60];
+
+       errcnt = 0;
+
+       rc = ndmca_robot_obtain_info (sess);
+       if (rc) return rc;
+
+       if (sess->control_acb.job.remedy_all) {
+               first_dte_addr = smc->elem_aa.dte_addr;
+               n_dte_addr = smc->elem_aa.dte_count;
+       } else {
+               n_dte_addr = 1;
+               if (sess->control_acb.job.drive_addr_given) {
+                       first_dte_addr = sess->control_acb.job.drive_addr;
+               } else {
+                       first_dte_addr = smc->elem_aa.dte_addr;
+               }
+       }
+
+       for (i = 0; i < n_dte_addr; i++) {
+               edp = ndmca_robot_find_element (sess, first_dte_addr+i);
+
+               if (!edp->Full)
+                       continue;
+
+               sprintf (prefix, "drive @%d not empty", edp->element_address);
+
+               if (!edp->SValid) {
+                       ndmalogf (sess, 0, 1, "%s, invalid source", prefix);
+                       errcnt++;
+                       continue;
+               }
+
+               sprintf (NDMOS_API_STREND(prefix), ", src @%d",
+                                                       edp->src_se_addr);
+
+               edp2 = ndmca_robot_find_element (sess, edp->src_se_addr);
+
+               if (edp2->element_type_code != SMC_ELEM_TYPE_SE) {
+                       ndmalogf (sess, 0, 1, "%s, not slot", prefix);
+                       errcnt++;
+                       continue;
+               }
+
+               if (edp2->Full) {
+                       ndmalogf (sess, 0, 1, "%s, but slot Full", prefix);
+                       errcnt++;
+                       continue;
+               }
+
+               rc = ndmca_robot_move (sess,
+                               edp->element_address, edp->src_se_addr);
+               if (rc) {
+                       ndmalogf (sess, 0, 1, "%s, move failed", prefix);
+                       errcnt++;
+                       continue;
+               }
+       }
+
+       return errcnt;
+}
+
+
+
+/*
+ * ndmca_robot_query() incrementally obtains info so that we
+ * can print progress.
+ */
+
+int
+ndmca_robot_query (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       int                     rc;
+       unsigned int            i;
+       char                    buf[100];
+       char                    lnbuf[30];
+       int                     lineno, nline = 1;
+
+       ndmalogqr (sess, "  Type");
+
+       rc = smc_inquire (smc);
+       if (rc) {
+               ndmalogqr (sess, "    ERROR smc_inquire(): %s", smc->errmsg);
+       } else {
+               ndmalogqr (sess, "    '%s'", smc->ident);
+       }
+
+
+       ndmalogqr (sess, "  Elements");
+       rc = smc_get_elem_aa (smc);
+       if (rc) {
+               ndmalogqr (sess, "    ERROR smc_get_elem_aa(): %s", smc->errmsg);
+       } else {
+               strcpy (lnbuf, "    ");
+               for (lineno = 0, nline = 1; lineno < nline; lineno++) {
+                       rc = smc_pp_element_address_assignments (&smc->elem_aa,
+                                                               lineno, buf);
+                       if (rc < 0) {
+                               strcpy (buf, "PP-ERROR");
+                       }
+                       nline = rc;
+                       ndmalogqr (sess, "%s %s", lnbuf, buf);
+               }
+       }
+
+       ndmalogqr (sess, "  Status");
+       rc = smc_read_elem_status (smc);
+       if (rc) {
+               ndmalogqr (sess, "    ERROR smc_read_elem_status(): %s", smc->errmsg);
+       } else {
+               ndmalogqr (sess, "    E#  Addr Type Status");
+               ndmalogqr (sess, "    --  ---- ---- ---------------------");
+               for (i = 0; i < smc->n_elem_desc; i++) {
+                       struct smc_element_descriptor * edp;
+
+                       edp = &smc->elem_desc[i];
+
+                       for (lineno = 0, nline = 1; lineno < nline; lineno++) {
+                               rc = smc_pp_element_descriptor (edp,
+                                                               lineno, buf);
+
+                               if (lineno == 0)
+                                       sprintf (lnbuf, "    %2d ", i+1);
+                               else
+                                       sprintf (lnbuf, "       ");
+
+                               if (rc < 0) {
+                                       strcpy (buf, "PP-ERROR");
+                               }
+                               nline = rc;
+                               ndmalogqr (sess, "%s %s", lnbuf, buf);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+int
+ndmca_robot_verify_media (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
+       int                     rc;
+       struct ndmmedia *       me;
+       struct smc_element_descriptor *edp;
+       int                     i;
+       unsigned int            j;
+       int                     errcnt = 0;
+
+       rc = ndmca_robot_obtain_info (sess);
+       if (rc) return rc;
+
+       for (i = 0; i < mtab->n_media; i++) {
+               me = &mtab->media[i];
+
+               if (! me->valid_slot) {
+                       me->slot_missing = 1;
+                       errcnt++;
+                       continue;       /* what now */
+               }
+
+               for (j = 0; j < smc->n_elem_desc; j++) {
+                       edp = &smc->elem_desc[j];
+
+                       if (edp->element_type_code != SMC_ELEM_TYPE_SE)
+                               continue;
+
+                       if (edp->element_address != me->slot_addr)
+                               continue;
+
+                       if (!edp->Full) {
+                               me->slot_empty = 1;
+                               errcnt++;
+                       } else {
+                               me->slot_empty = 0;
+                       }
+                       break;
+               }
+               if (j >= smc->n_elem_desc) {
+                       me->slot_bad = 1;
+                       errcnt++;
+               }
+       }
+
+       return errcnt;
+}
+
+/*
+ * For NDM_JOB_OP_LIST_LABELS, fill in media_tab based on non-empty slots.
+ * Note: this might REALLY nerf on a cleaning cartridge.
+ */
+
+int
+ndmca_robot_synthesize_media (struct ndm_session *sess)
+{
+       struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+       struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
+       int                     rc;
+       struct ndmmedia *       me;
+       struct smc_element_descriptor *edp;
+       unsigned int            i;
+
+       rc = ndmca_robot_obtain_info (sess);
+       if (rc) return rc;
+
+       for (i = 0; i < smc->n_elem_desc; i++) {
+               edp = &smc->elem_desc[i];
+
+               if (edp->element_type_code != SMC_ELEM_TYPE_SE)
+                       continue;
+
+               if (!edp->Full)
+                       continue;
+
+               me = &mtab->media[mtab->n_media++];
+               NDMOS_MACRO_ZEROFILL (me);
+
+               me->valid_slot = 1;
+               me->slot_addr = edp->element_address;
+       }
+
+       return 0;
+}
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */