Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndma_ctrl_robot.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36
37
38 #include "ndmagents.h"
39
40
41 #ifndef NDMOS_OPTION_NO_CONTROL_AGENT
42
43
44 int
45 ndmca_robot_issue_scsi_req (struct smc_ctrl_block *smc)
46 {
47         struct ndmconn *        conn = (struct ndmconn *) smc->app_data;
48         struct smc_scsi_req *   sr = &smc->scsi_req;
49         int                     rc;
50
51         rc = ndmscsi_execute (conn, (struct ndmscsi_request *) sr, 0);
52         return rc;
53 }
54
55
56 int
57 ndmca_robot_prep_target (struct ndm_session *sess)
58 {
59         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
60         int                     rc;
61
62         NDMOS_MACRO_ZEROFILL (smc);
63
64         smc->app_data = sess->plumb.robot;
65         smc->issue_scsi_req = ndmca_robot_issue_scsi_req;
66
67         rc = ndmscsi_use (sess->plumb.robot,
68                                 &sess->control_acb.job.robot_target);
69         if (rc) return rc;
70
71         return 0;
72 }
73
74 int
75 ndmca_robot_obtain_info (struct ndm_session *sess)
76 {
77         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
78         int                     rc;
79
80         rc = smc_inquire (smc);
81         if (rc) return rc;
82
83         rc = smc_get_elem_aa (smc);
84         if (rc) return rc;
85
86         rc = smc_read_elem_status (smc);
87         if (rc) return rc;
88
89         return 0;
90 }
91
92 int
93 ndmca_robot_init_elem_status (struct ndm_session *sess)
94 {
95         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
96         int                     rc;
97
98         ndmalogf (sess, 0, 1,
99             "Commanding robot to initialize element status (take inventory)");
100
101         rc = smc_init_elem_status (smc);
102         if (rc) {
103                 ndmalogf (sess, 0, 0, "init-elem-status failed");
104                 return rc;
105         }
106
107         return 0;
108 }
109
110 int
111 ndmca_robot_startup (struct ndm_session *sess)
112 {
113         int             rc;
114
115         if (!sess->control_acb.job.have_robot)
116                 return -1;      /* Huh? why were we called */
117
118         rc = ndmca_connect_robot_agent(sess);
119         if (rc) return rc;
120
121         rc = ndmca_robot_prep_target(sess);
122         if (rc) return rc;
123
124         return 0;
125 }
126
127 int
128 ndmca_robot_move (struct ndm_session *sess, int src_addr, int dst_addr)
129 {
130         struct ndm_control_agent *ca = &sess->control_acb;
131         struct smc_ctrl_block * smc = &ca->smc_cb;
132         int                     rc;
133         unsigned int            t;
134
135         ndmalogf (sess, 0, 2, "robot moving @%d to @%d",
136                         src_addr, dst_addr);
137
138         rc = -1;
139         for (t = 0; t <= ca->job.robot_timeout; t += 10) {
140                 if (t > 0) {
141                         ndmalogf (sess, 0, 2,
142                                 "Pausing ten seconds before retry (%d/%d)",
143                                 t, ca->job.robot_timeout);
144                         sleep (10);
145                 }
146                 rc = smc_move (smc, src_addr, dst_addr,
147                                         0, smc->elem_aa.mte_addr);
148                 if (rc == 0) break;
149         }
150
151         if (rc == 0) {
152                 ndmalogf (sess, 0, 2, "robot move OK @%d to @%d",
153                                 src_addr, dst_addr);
154         } else {
155                 ndmalogf (sess, 0, 2, "robot move BAD @%d to @%d",
156                         src_addr, dst_addr);
157         }
158
159         return rc;
160 }
161
162 int
163 ndmca_robot_load (struct ndm_session *sess, int slot_addr)
164 {
165         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
166         unsigned                dte_addr = smc->elem_aa.dte_addr;
167         int                     rc;
168
169         if (sess->control_acb.job.drive_addr_given)
170                 dte_addr = sess->control_acb.job.drive_addr;
171
172         ndmalogf (sess, 0, 1,
173                         "Commanding robot to load slot @%d into drive @%d",
174                         slot_addr, dte_addr);
175
176         rc = ndmca_robot_move (sess, slot_addr, dte_addr);
177
178         return rc;
179 }
180
181 int
182 ndmca_robot_unload (struct ndm_session *sess, int slot_addr)
183 {
184         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
185         int                     dte_addr = smc->elem_aa.dte_addr;
186         int                     rc;
187
188         if (sess->control_acb.job.drive_addr_given)
189                 dte_addr = sess->control_acb.job.drive_addr;
190
191         /* tricky part -- some (most?) robots need the drive to eject */
192
193         ndmalogf (sess, 0, 1,
194                         "Commanding robot to unload drive @%d to slot @%d",
195                         dte_addr, slot_addr);
196
197         rc = ndmca_robot_move (sess, dte_addr, slot_addr);
198
199         return rc;
200 }
201
202
203 struct smc_element_descriptor *
204 ndmca_robot_find_element (struct ndm_session *sess, int element_address)
205 {
206         struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
207         unsigned int                    i;
208         struct smc_element_descriptor * edp;
209
210         for (i = 0; i < smc->n_elem_desc; i++) {
211                 edp = &smc->elem_desc[i];
212                 if (edp->element_address == element_address)
213                         return edp;
214         }
215
216         return 0;
217 }
218
219 int
220 ndmca_robot_check_ready (struct ndm_session *sess)
221 {
222         struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
223         unsigned                        first_dte_addr;
224         unsigned                        n_dte_addr;
225         int                             rc;
226         unsigned int                    i;
227         int                             errcnt = 0;
228         struct smc_element_descriptor * edp;
229
230         rc = ndmca_robot_obtain_info (sess);
231         if (rc) return rc;
232
233         if (sess->control_acb.job.remedy_all) {
234                 first_dte_addr = smc->elem_aa.dte_addr;
235                 n_dte_addr = smc->elem_aa.dte_count;
236         } else {
237                 n_dte_addr = 1;
238                 if (sess->control_acb.job.drive_addr_given) {
239                         first_dte_addr = sess->control_acb.job.drive_addr;
240                 } else {
241                         first_dte_addr = smc->elem_aa.dte_addr;
242                 }
243         }
244
245         for (i = 0; i < n_dte_addr; i++) {
246                 edp = ndmca_robot_find_element (sess, first_dte_addr+i);
247
248                 if (!edp->Full)
249                         continue;
250
251                 ndmalogf (sess, 0, 1, "tape drive @%d not empty",
252                                 edp->element_address);
253                 errcnt++;
254         }
255
256         return errcnt;
257 }
258
259 int
260 ndmca_robot_remedy_ready (struct ndm_session *sess)
261 {
262         struct smc_ctrl_block *         smc = &sess->control_acb.smc_cb;
263         int                             rc;
264         unsigned int                    i;
265         int                             errcnt;
266         struct smc_element_descriptor * edp;
267         struct smc_element_descriptor * edp2;
268         unsigned                        first_dte_addr;
269         unsigned                        n_dte_addr;
270         char                            prefix[60];
271
272         errcnt = 0;
273
274         rc = ndmca_robot_obtain_info (sess);
275         if (rc) return rc;
276
277         if (sess->control_acb.job.remedy_all) {
278                 first_dte_addr = smc->elem_aa.dte_addr;
279                 n_dte_addr = smc->elem_aa.dte_count;
280         } else {
281                 n_dte_addr = 1;
282                 if (sess->control_acb.job.drive_addr_given) {
283                         first_dte_addr = sess->control_acb.job.drive_addr;
284                 } else {
285                         first_dte_addr = smc->elem_aa.dte_addr;
286                 }
287         }
288
289         for (i = 0; i < n_dte_addr; i++) {
290                 edp = ndmca_robot_find_element (sess, first_dte_addr+i);
291
292                 if (!edp->Full)
293                         continue;
294
295                 sprintf (prefix, "drive @%d not empty", edp->element_address);
296
297                 if (!edp->SValid) {
298                         ndmalogf (sess, 0, 1, "%s, invalid source", prefix);
299                         errcnt++;
300                         continue;
301                 }
302
303                 sprintf (NDMOS_API_STREND(prefix), ", src @%d",
304                                                         edp->src_se_addr);
305
306                 edp2 = ndmca_robot_find_element (sess, edp->src_se_addr);
307
308                 if (edp2->element_type_code != SMC_ELEM_TYPE_SE) {
309                         ndmalogf (sess, 0, 1, "%s, not slot", prefix);
310                         errcnt++;
311                         continue;
312                 }
313
314                 if (edp2->Full) {
315                         ndmalogf (sess, 0, 1, "%s, but slot Full", prefix);
316                         errcnt++;
317                         continue;
318                 }
319
320                 rc = ndmca_robot_move (sess,
321                                 edp->element_address, edp->src_se_addr);
322                 if (rc) {
323                         ndmalogf (sess, 0, 1, "%s, move failed", prefix);
324                         errcnt++;
325                         continue;
326                 }
327         }
328
329         return errcnt;
330 }
331
332
333
334 /*
335  * ndmca_robot_query() incrementally obtains info so that we
336  * can print progress.
337  */
338
339 int
340 ndmca_robot_query (struct ndm_session *sess)
341 {
342         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
343         int                     rc;
344         unsigned int            i;
345         char                    buf[100];
346         char                    lnbuf[30];
347         int                     lineno, nline = 1;
348
349         ndmalogqr (sess, "  Type");
350
351         rc = smc_inquire (smc);
352         if (rc) {
353                 ndmalogqr (sess, "    ERROR smc_inquire(): %s", smc->errmsg);
354         } else {
355                 ndmalogqr (sess, "    '%s'", smc->ident);
356         }
357
358
359         ndmalogqr (sess, "  Elements");
360         rc = smc_get_elem_aa (smc);
361         if (rc) {
362                 ndmalogqr (sess, "    ERROR smc_get_elem_aa(): %s", smc->errmsg);
363         } else {
364                 strcpy (lnbuf, "    ");
365                 for (lineno = 0, nline = 1; lineno < nline; lineno++) {
366                         rc = smc_pp_element_address_assignments (&smc->elem_aa,
367                                                                 lineno, buf);
368                         if (rc < 0) {
369                                 strcpy (buf, "PP-ERROR");
370                         }
371                         nline = rc;
372                         ndmalogqr (sess, "%s %s", lnbuf, buf);
373                 }
374         }
375
376         ndmalogqr (sess, "  Status");
377         rc = smc_read_elem_status (smc);
378         if (rc) {
379                 ndmalogqr (sess, "    ERROR smc_read_elem_status(): %s", smc->errmsg);
380         } else {
381                 ndmalogqr (sess, "    E#  Addr Type Status");
382                 ndmalogqr (sess, "    --  ---- ---- ---------------------");
383                 for (i = 0; i < smc->n_elem_desc; i++) {
384                         struct smc_element_descriptor * edp;
385
386                         edp = &smc->elem_desc[i];
387
388                         for (lineno = 0, nline = 1; lineno < nline; lineno++) {
389                                 rc = smc_pp_element_descriptor (edp,
390                                                                 lineno, buf);
391
392                                 if (lineno == 0)
393                                         sprintf (lnbuf, "    %2d ", i+1);
394                                 else
395                                         sprintf (lnbuf, "       ");
396
397                                 if (rc < 0) {
398                                         strcpy (buf, "PP-ERROR");
399                                 }
400                                 nline = rc;
401                                 ndmalogqr (sess, "%s %s", lnbuf, buf);
402                         }
403                 }
404         }
405
406         return 0;
407 }
408
409
410 int
411 ndmca_robot_verify_media (struct ndm_session *sess)
412 {
413         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
414         struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
415         int                     rc;
416         struct ndmmedia *       me;
417         struct smc_element_descriptor *edp;
418         int                     i;
419         unsigned int            j;
420         int                     errcnt = 0;
421
422         rc = ndmca_robot_obtain_info (sess);
423         if (rc) return rc;
424
425         for (i = 0; i < mtab->n_media; i++) {
426                 me = &mtab->media[i];
427
428                 if (! me->valid_slot) {
429                         me->slot_missing = 1;
430                         errcnt++;
431                         continue;       /* what now */
432                 }
433
434                 for (j = 0; j < smc->n_elem_desc; j++) {
435                         edp = &smc->elem_desc[j];
436
437                         if (edp->element_type_code != SMC_ELEM_TYPE_SE)
438                                 continue;
439
440                         if (edp->element_address != me->slot_addr)
441                                 continue;
442
443                         if (!edp->Full) {
444                                 me->slot_empty = 1;
445                                 errcnt++;
446                         } else {
447                                 me->slot_empty = 0;
448                         }
449                         break;
450                 }
451                 if (j >= smc->n_elem_desc) {
452                         me->slot_bad = 1;
453                         errcnt++;
454                 }
455         }
456
457         return errcnt;
458 }
459
460 /*
461  * For NDM_JOB_OP_LIST_LABELS, fill in media_tab based on non-empty slots.
462  * Note: this might REALLY nerf on a cleaning cartridge.
463  */
464
465 int
466 ndmca_robot_synthesize_media (struct ndm_session *sess)
467 {
468         struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
469         struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
470         int                     rc;
471         struct ndmmedia *       me;
472         struct smc_element_descriptor *edp;
473         unsigned int            i;
474
475         rc = ndmca_robot_obtain_info (sess);
476         if (rc) return rc;
477
478         for (i = 0; i < smc->n_elem_desc; i++) {
479                 edp = &smc->elem_desc[i];
480
481                 if (edp->element_type_code != SMC_ELEM_TYPE_SE)
482                         continue;
483
484                 if (!edp->Full)
485                         continue;
486
487                 me = &mtab->media[mtab->n_media++];
488                 NDMOS_MACRO_ZEROFILL (me);
489
490                 me->valid_slot = 1;
491                 me->slot_addr = edp->element_address;
492         }
493
494         return 0;
495 }
496 #endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */