Imported Upstream version 3.1.0
[debian/amanda] / changer-src / scsi-linux.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 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: scsi-linux.c,v 1.30 2006/07/06 11:57:28 martinea Exp $
28  *
29  * Interface to execute SCSI commands on Linux
30  *
31  * Copyright (c) Thomas Hepper th@ant.han.de
32  */
33
34
35 #include "amanda.h"
36
37 #ifdef HAVE_SCSI_SG_H
38 #include <scsi/sg.h>
39 #define LINUX_SG
40 #endif
41
42 #ifdef HAVE_SYS_MTIO_H
43 #include <sys/mtio.h>
44 #endif
45
46 #include <scsi-defs.h>
47
48 extern OpenFiles_T *pDev;
49
50 void SCSI_OS_Version(void)
51 {
52 #ifndef lint
53    static char rcsid[] = "$Id: scsi-linux.c,v 1.30 2006/07/06 11:57:28 martinea Exp $";
54    DebugPrint(DEBUG_ERROR, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
55 #endif
56 }
57
58 int SCSI_CloseDevice(int DeviceFD)
59 {
60   int ret = 0;
61   
62   if (pDev[DeviceFD].devopen == 1)
63     {
64       pDev[DeviceFD].devopen = 0;
65       ret = close(pDev[DeviceFD].fd);
66     }
67
68   return(ret);
69 }
70
71 /* Open a device to talk to an scsi device, either per ioctl, or
72  * direct writing....
73  * Return:
74  * 0 -> error
75  * 1 -> OK
76  *
77  * TODO:
78  * Define some readable defs for the falgs which can be set (like in the AIX dreiver)
79  *
80  */
81 #ifdef LINUX_SG
82 int SCSI_OpenDevice(int ip)
83 {
84   int DeviceFD;
85   int i;
86   int timeout;
87   struct stat pstat;
88   char *buffer = NULL ;           /* Will contain the device name after checking */
89   int openmode = O_RDONLY;
90
91   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### START SCSI_OpenDevice\n"));
92   if (pDev[ip].inqdone == 0)
93     {
94       pDev[ip].inqdone = 1;
95       if (strncmp("/dev/sg", pDev[ip].dev, 7) != 0) /* Check if no sg device for an link .... */
96         {
97           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : checking if %s is a sg device\n"), pDev[ip].dev);
98           if (lstat(pDev[ip].dev, &pstat) != -1)
99             {
100               if (S_ISLNK(pstat.st_mode) == 1)
101                 {
102                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : is a link, checking destination\n"));
103                   if ((buffer = (char *)malloc(513)) == NULL)
104                     {
105                       DebugPrint(DEBUG_ERROR, SECTION_SCSI,_("SCSI_OpenDevice : malloc failed\n"));
106                       return(0);
107                     }
108                   memset(buffer, 0, 513);
109                   if (( i = readlink(pDev[ip].dev, buffer, 512)) == -1)
110                     {
111                       if (errno == ENAMETOOLONG )
112                         {
113                         } else {
114                           pDev[ip].SCSI = 0;
115                         }
116                     }
117                   if ( i >= 7)
118                     {
119                       if (strncmp("/dev/sg", buffer, 7) == 0)
120                         {
121                           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : link points to %s\n"), buffer) ;
122                           pDev[ip].flags = 1;
123                         }
124                     }
125                 } else {/* S_ISLNK(pstat.st_mode) == 1 */
126                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("No link %s\n"), pDev[ip].dev) ;
127                   buffer = stralloc(pDev[ip].dev);
128                 }
129             } else {/* lstat(DeviceName, &pstat) != -1 */ 
130               DebugPrint(DEBUG_ERROR, SECTION_SCSI,_("can't stat device %s\n"), pDev[ip].dev);
131               return(0);
132             }
133         } else {
134           buffer = stralloc(pDev[ip].dev);
135           pDev[ip].flags = 1;
136         }
137       
138       if (pDev[ip].flags == 1)
139         {
140           openmode = O_RDWR;
141         }
142       
143       DebugPrint(DEBUG_INFO, SECTION_SCSI,_("Try to open %s\n"), buffer);
144       if ((DeviceFD = open(buffer, openmode)) >= 0)
145         {
146           pDev[ip].avail = 1;
147           pDev[ip].devopen = 1;
148           pDev[ip].fd = DeviceFD;
149         } else {
150           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice open failed\n"));
151           amfree(buffer);
152           return(0);
153         }
154       
155       DebugPrint(DEBUG_INFO, SECTION_SCSI,_("done\n"));
156       if ( pDev[ip].flags == 1)
157         {
158           pDev[ip].SCSI = 1;
159         }
160       
161       pDev[ip].dev = buffer;
162       if (pDev[ip].SCSI == 1)
163         {
164           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : use SG interface\n"));
165           if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0) 
166             {
167               DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : current timeout %d\n"), timeout);
168               timeout = 60000;
169               if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
170                 {
171                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : timeout set to %d\n"), timeout);
172                 }
173             }
174           pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
175           if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
176             {
177               if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
178                 {
179                   for (i=0;i < 16;i++)
180                     pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
181                   for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]); i--)
182                     {
183                       pDev[ip].ident[i] = '\0';
184                     }
185                   pDev[ip].SCSI = 1;
186
187                   if (pDev[ip].inquiry->type == TYPE_TAPE)
188                   {
189                           pDev[ip].type = stralloc("tape");
190                   }
191
192                   if (pDev[ip].inquiry->type == TYPE_CHANGER)
193                   {
194                           pDev[ip].type = stralloc("changer");
195                   }
196
197                   PrintInquiry(pDev[ip].inquiry);
198                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
199                   return(1);
200                 } else {
201                   close(DeviceFD);
202                   amfree(pDev[ip].inquiry);
203                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (0)\n"));
204                   return(0);
205                 }
206             } else {
207               pDev[ip].SCSI = 0;
208               pDev[ip].devopen = 0;
209               close(DeviceFD);
210               amfree(pDev[ip].inquiry);
211               pDev[ip].inquiry = NULL;
212               DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
213               return(1);
214             }
215         } else /* if (pDev[ip].SCSI == 1) */ {  
216           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("Device not capable for SCSI commands\n"));
217           pDev[ip].SCSI = 0;
218           pDev[ip].devopen = 0;
219           close(DeviceFD);
220           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
221           return(1);
222         }
223     } else { /* if (pDev[ip].inqdone == 0) */
224       if (pDev[ip].flags == 1)
225         {
226           openmode = O_RDWR;
227         } else {
228           openmode = O_RDONLY;
229         }
230       if ((DeviceFD = open(pDev[ip].dev, openmode)) >= 0)
231         {
232           pDev[ip].devopen = 1;
233           pDev[ip].fd = DeviceFD;
234           if (pDev[ip].flags == 1)
235             {
236               if ((timeout = ioctl(pDev[ip].fd, SG_GET_TIMEOUT)) > 0) 
237                 {
238                   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : current timeout %d\n"), timeout);
239                   timeout = 60000;
240                   if (ioctl(pDev[ip].fd, SG_SET_TIMEOUT, &timeout) == 0)
241                     {
242                       DebugPrint(DEBUG_INFO, SECTION_SCSI,_("SCSI_OpenDevice : timeout set to %d\n"), timeout);
243                     }
244                 }
245             }
246           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice (1)\n"));
247           return(1);
248         } else {
249           DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice open failed\n"));
250           return(0);
251         }
252     }
253   DebugPrint(DEBUG_INFO, SECTION_SCSI,_("##### STOP SCSI_OpenDevice should not happen !!\n"));
254   return(0);
255 }
256
257 #define SCSI_OFF SIZEOF(struct sg_header)
258 int SCSI_ExecuteCommand(int DeviceFD,
259                         Direction_T Direction,
260                         CDB_T CDB,
261                         size_t CDB_Length,
262                         void *DataBuffer,
263                         size_t DataBufferLength,
264                         RequestSense_T *pRequestSense,
265                         size_t RequestSenseLength)
266 {
267   struct sg_header *psg_header;
268   char *buffer;
269   size_t osize = 0;
270   ssize_t status;
271
272   /* Basic sanity checks */
273   assert(CDB_Length <= UCHAR_MAX);
274   assert(RequestSenseLength <= UCHAR_MAX);
275
276   /* Clear buffer for cases where sense is not returned */
277   memset(pRequestSense, 0, RequestSenseLength);
278
279   if (pDev[DeviceFD].avail == 0)
280     {
281       return(-1);
282     }
283
284   if (pDev[DeviceFD].devopen == 0)
285       if (SCSI_OpenDevice(DeviceFD) == 0)
286           return(-1);
287   
288   if (SCSI_OFF + CDB_Length + DataBufferLength > 4096) 
289     {
290       SCSI_CloseDevice(DeviceFD);
291       return(-1);
292     }
293
294   buffer = (char *)malloc(SCSI_OFF + CDB_Length + DataBufferLength);
295   if (buffer == NULL)
296     {
297       dbprintf(_("SCSI_ExecuteCommand memory allocation failure.\n"));
298       SCSI_CloseDevice(DeviceFD);
299       return(-1);
300     }
301   memset(buffer, 0, SCSI_OFF + CDB_Length + DataBufferLength);
302   memcpy(buffer + SCSI_OFF, CDB, CDB_Length);
303   
304   psg_header = (struct sg_header *)buffer;
305   if (CDB_Length >= 12)
306     {
307       psg_header->twelve_byte = 1;
308     } else {
309       psg_header->twelve_byte = 0;
310     }
311   psg_header->result = 0;
312   psg_header->reply_len = (int)(SCSI_OFF + DataBufferLength);
313   
314   switch (Direction)
315     {
316     case Input:
317       osize = 0;
318       break;
319     case Output:
320       osize = DataBufferLength;
321       break;
322     }
323   
324   DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
325   
326   status = write(pDev[DeviceFD].fd, buffer, SCSI_OFF + CDB_Length + osize);
327   if ( (status < (ssize_t)0) ||
328        (status != (ssize_t)(SCSI_OFF + CDB_Length + osize)) ||
329        (psg_header->result != 0)) 
330     {
331       dbprintf(_("SCSI_ExecuteCommand error send \n"));
332       SCSI_CloseDevice(DeviceFD);
333       amfree(buffer);
334       return(SCSI_ERROR);
335     }
336   
337   memset(buffer, 0, SCSI_OFF + DataBufferLength);
338   status = read(pDev[DeviceFD].fd, buffer, SCSI_OFF + DataBufferLength);
339   memset(pRequestSense, 0, RequestSenseLength);
340   memcpy(pRequestSense, psg_header->sense_buffer, 16);
341   
342   if ( (status < 0) ||
343        (status != (ssize_t)(SCSI_OFF + DataBufferLength)) || 
344        (psg_header->result != 0)) 
345     { 
346       dbprintf(_("SCSI_ExecuteCommand error read \n"));
347       dbprintf(_("Status %zd (%zd) %2X\n"), status, SCSI_OFF + DataBufferLength,psg_header->result );
348       SCSI_CloseDevice(DeviceFD);
349       amfree(buffer);
350       return(SCSI_ERROR);
351     }
352
353   if (DataBufferLength)
354     {
355        memcpy(DataBuffer, buffer + SCSI_OFF, DataBufferLength);
356     }
357
358   SCSI_CloseDevice(DeviceFD);
359   amfree(buffer);
360   return(SCSI_OK);
361 }
362
363 #else
364
365 static inline int min(int x, int y)
366 {
367   return (x < y ? x : y);
368 }
369
370
371 static inline int max(int x, int y)
372 {
373   return (x > y ? x : y);
374 }
375
376 int SCSI_OpenDevice(int ip)
377 {
378   int DeviceFD;
379   int i;
380
381   if (pDev[ip].inqdone == 0)
382     {
383       pDev[ip].inqdone = 1;
384       if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
385         {
386           pDev[ip].avail = 1;
387           pDev[ip].fd = DeviceFD;
388           pDev[ip].SCSI = 0;
389           pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
390           dbprintf(_("SCSI_OpenDevice : use ioctl interface\n"));
391           if (SCSI_Inquiry(ip, pDev[ip].inquiry, (u_char)INQUIRY_SIZE) == 0)
392             {
393               if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
394                 {
395                   for (i=0;i < 16 && pDev[ip].inquiry->prod_ident[i] != ' ';i++)
396                     pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
397                   pDev[ip].ident[i] = '\0';
398                   pDev[ip].SCSI = 1;
399                   PrintInquiry(pDev[ip].inquiry);
400                   return(1);
401                 } else {
402                   amfree(pDev[ip].inquiry);
403                   close(DeviceFD);
404                   return(0);
405                 }
406             } else {
407               close(DeviceFD);
408               amfree(pDev[ip].inquiry);
409               pDev[ip].inquiry = NULL;
410               return(1);
411             }
412         }
413       return(1); 
414     } else {
415       if ((DeviceFD = open(pDev[ip].dev, O_RDWR)) >= 0)
416         {
417           pDev[ip].fd = DeviceFD;
418           pDev[ip].devopen = 1;
419           return(1);
420         } else {
421           pDev[ip].devopen = 0;
422           return(0);
423         }
424     }
425 }
426
427 int SCSI_ExecuteCommand(int DeviceFD,
428                         Direction_T Direction,
429                         CDB_T CDB,
430                         int CDB_Length,
431                         void *DataBuffer,
432                         int DataBufferLength,
433                         RequestSense_T *pRequestSense,
434                         int RequestSenseLength)
435 {
436   unsigned char *Command;
437   int Zero = 0, Result;
438  
439   if (pDev[DeviceFD].avail == 0)
440     {
441       return(SCSI_ERROR);
442     }
443
444   if (pDev[DeviceFD].devopen == 0)
445     {
446       if (SCSI_OpenDevice(DeviceFD) == 0)
447           return(-1);
448     }
449
450   memset(pRequestSense, 0, RequestSenseLength);
451   switch (Direction)
452     {
453     case Input:
454       Command = (unsigned char *)
455         malloc(8 + max(DataBufferLength, RequestSenseLength));
456       memcpy(&Command[0], &Zero, 4);
457       memcpy(&Command[4], &DataBufferLength, 4);
458       memcpy(&Command[8], CDB, CDB_Length);
459       break;
460     case Output:
461       Command = (unsigned char *)
462         malloc(8 + max(CDB_Length + DataBufferLength, RequestSenseLength));
463       memcpy(&Command[0], &DataBufferLength, 4);
464       memcpy(&Command[4], &Zero, 4);
465       memcpy(&Command[8], CDB, CDB_Length);
466       memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
467       break;
468     }
469   
470   DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
471   
472   Result = ioctl(pDev[DeviceFD].fd, SCSI_IOCTL_SEND_COMMAND, Command);
473   if (Result != 0)
474     memcpy(pRequestSense, &Command[8], RequestSenseLength);
475   else if (Direction == Input)
476     memcpy(DataBuffer, &Command[8], DataBufferLength);
477   amfree(Command);
478   SCSI_CloseDevice(DeviceFD);
479
480   switch(Result)
481     {
482       case 0:
483         return(SCSI_OK);
484         break;
485     default:
486       return(SCSI_SENSE);
487       break;
488     }
489 }
490 #endif
491
492 /*
493  * Send the command to the device with the
494  * ioctl interface
495  */
496 int Tape_Ioctl( int DeviceFD, int command)
497 {
498   struct mtop mtop;
499   int ret = 0;
500
501   if (pDev[DeviceFD].devopen == 0)
502     {
503       if (SCSI_OpenDevice(DeviceFD) == 0)
504           return(-1);
505     }
506
507   switch (command)
508     {
509     case IOCTL_EJECT:
510       mtop.mt_op = MTOFFL;
511       mtop.mt_count = 1;
512       break;
513      default:
514       break;
515     }
516
517   if (ioctl(pDev[DeviceFD].fd , MTIOCTOP, &mtop) != 0)
518     {
519       dbprintf(_("Tape_Ioctl error ioctl %s\n"),strerror(errno));
520       SCSI_CloseDevice(DeviceFD);
521       return(-1);
522     }
523
524   SCSI_CloseDevice(DeviceFD);
525   return(ret);  
526 }
527
528 int Tape_Status( int DeviceFD)
529 {
530   struct mtget mtget;
531   int ret = 0;
532
533   memset(&mtget, 0, SIZEOF(mtget));
534   if (pDev[DeviceFD].devopen == 0)
535     {
536       if (SCSI_OpenDevice(DeviceFD) == 0)
537           return(-1);
538     }
539
540   if (ioctl(pDev[DeviceFD].fd , MTIOCGET, &mtget) != 0)
541   {
542      DebugPrint(DEBUG_ERROR, SECTION_TAPE,_("Tape_Status error ioctl %s\n"),
543                 strerror(errno));
544      SCSI_CloseDevice(DeviceFD);
545      return(-1);
546   }
547
548   DebugPrint(DEBUG_INFO, SECTION_TAPE,_("ioctl -> mtget.mt_gstat %lX\n"),mtget.mt_gstat);
549   if (GMT_ONLINE(mtget.mt_gstat))
550     {
551       ret = TAPE_ONLINE;
552     }
553   
554   if (GMT_BOT(mtget.mt_gstat))
555     {
556       ret = ret | TAPE_BOT;
557     }
558   
559   if (GMT_EOT(mtget.mt_gstat))
560     {
561       ret = ret | TAPE_EOT;
562     }
563   
564   if (GMT_WR_PROT(mtget.mt_gstat))
565     {
566       ret = ret | TAPE_WR_PROT;
567     }
568   
569   if (GMT_DR_OPEN(mtget.mt_gstat))
570     {
571       ret = ret | TAPE_NOT_LOADED;
572     }
573   
574   SCSI_CloseDevice(DeviceFD);
575   return(ret); 
576 }
577
578 /*
579  * This functions scan all /dev/sg* devices
580  * It opens the device an print the result of the inquiry 
581  *
582  */
583 int ScanBus(int print)
584 {
585   DIR *dir;
586   struct dirent *dirent;
587   int count = 0;
588
589   if ((dir = opendir("/dev/")) == NULL)
590     {
591       dbprintf(_("/dev/ error: %s"), strerror(errno));
592       return 0;
593     }
594
595   while ((dirent = readdir(dir)) != NULL)
596     {
597       if (strstr(dirent->d_name, "sg") != NULL)
598       {
599         pDev[count].dev = malloc(10);
600         pDev[count].inqdone = 0;
601         g_snprintf(pDev[count].dev, SIZEOF(pDev[count].dev),
602             "/dev/%s", dirent->d_name);
603         if (OpenDevice(count,pDev[count].dev, "Scan", NULL ))
604           {
605             SCSI_CloseDevice(count);
606             pDev[count].inqdone = 0;
607             
608             if (print)
609               {
610                 g_printf(_("name /dev/%s "), dirent->d_name);
611                 
612                 switch (pDev[count].inquiry->type)
613                   {
614                   case TYPE_DISK:
615                     g_printf(_("Disk"));
616                     break;
617                   case TYPE_TAPE:
618                     g_printf(_("Tape"));
619                     break;
620                   case TYPE_PRINTER:
621                     g_printf(_("Printer"));
622                     break;
623                   case TYPE_PROCESSOR:
624                     g_printf(_("Processor"));
625                     break;
626                   case TYPE_WORM:
627                     g_printf(_("Worm"));
628                     break;
629                   case TYPE_CDROM:
630                     g_printf(_("Cdrom"));
631                     break;
632                   case TYPE_SCANNER:
633                     g_printf(_("Scanner"));
634                     break;
635                   case TYPE_OPTICAL:
636                     g_printf(_("Optical"));
637                     break;
638                   case TYPE_CHANGER:
639                     g_printf(_("Changer"));
640                     break;
641                   case TYPE_COMM:
642                     g_printf(_("Comm"));
643                     break;
644                   default:
645                     g_printf(_("unknown %d"),pDev[count].inquiry->type);
646                     break;
647                   }
648                 g_printf("\n");
649               }
650             count++;
651             g_printf(_("Count %d\n"),count);
652           } else {
653             amfree(pDev[count].dev);
654             pDev[count].dev=NULL;
655           }
656       }
657     }
658   return 0;
659 }
660 /*
661  * Local variables:
662  * indent-tabs-mode: nil
663  * c-file-style: gnu
664  * End:
665  */