1 /*
2   $Id: scan_devices.c,v 1.33 2008/06/16 19:45:44 flameeyes Exp $
3 
4   Copyright (C) 2004, 2005, 2007, 2008, 2009 Rocky Bernstein <rocky@gnu.org>
5   Copyright (C) 1998 Monty xiphmont@mit.edu
6 
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 /******************************************************************
22  *
23  * Autoscan for or verify presence of a CD-ROM device
24  *
25  ******************************************************************/
26 
27 #include "common_interface.h"
28 #include "low_interface.h"
29 #include "utils.h"
30 #include "cdio/mmc.h"
31 #include "cdio/util.h"
32 #include <limits.h>
33 #include <ctype.h>
34 
35 #ifdef HAVE_PWD_H
36 #include <pwd.h>
37 #endif
38 
39 #ifdef HAVE_SYS_STAT_H
40 #include <sys/stat.h>
41 #endif
42 
43 #ifndef PATH_MAX
44 #define PATH_MAX 4096
45 #endif
46 
47 static const char cdrom_devices[][32]={
48   "/dev/cdrom",
49   "/dev/cdroms/cdrom?",
50   "/dev/hd?",
51   "/dev/sg?",
52   "/dev/cdu31a",
53   "/dev/cdu535",
54   "/dev/sbpcd",
55   "/dev/sbpcd?",
56   "/dev/sonycd",
57   "/dev/mcd",
58   "/dev/sjcd",
59   /* "/dev/aztcd", timeout is too long */
60   "/dev/cm206cd",
61   "/dev/gscd",
62   "/dev/optcd",
63   ""};
64 
65 static cdrom_drive_t *
66 cdda_identify_device_cdio(CdIo_t *p_cdio, const char *psz_device,
67 			  int messagedest, char **ppsz_messages);
68 
69 /* Functions here look for a cdrom drive; full init of a drive type
70    happens in interface.c */
71 
72 cdrom_drive_t *
cdio_cddap_find_a_cdrom(int messagedest,char ** ppsz_messages)73 cdio_cddap_find_a_cdrom(int messagedest, char **ppsz_messages){
74   /* Brute force... */
75 
76   int i=0;
77   cdrom_drive_t *d;
78 
79   while(*cdrom_devices[i]!='\0'){
80 
81     /* is it a name or a pattern? */
82     char *pos;
83     if((pos=strchr(cdrom_devices[i],'?'))){
84       int j;
85       /* try first eight of each device */
86       for(j=0;j<4;j++){
87 	char *buffer=strdup(cdrom_devices[i]);
88 
89 	/* number, then letter */
90 
91 	buffer[pos-(cdrom_devices[i])]=j+48;
92 	if((d=cdda_identify(buffer, messagedest, ppsz_messages)))
93 	  return(d);
94 	idmessage(messagedest, ppsz_messages, "", NULL);
95 	buffer[pos-(cdrom_devices[i])]=j+97;
96 	if((d=cdda_identify(buffer, messagedest, ppsz_messages)))
97 	  return(d);
98 	idmessage(messagedest, ppsz_messages, "", NULL);
99 	free(buffer);
100       }
101     }else{
102       /* Name.  Go for it. */
103       if((d=cdda_identify(cdrom_devices[i], messagedest, ppsz_messages)))
104 	return(d);
105 
106       idmessage(messagedest, ppsz_messages, "", NULL);
107     }
108     i++;
109   }
110   {
111 #if defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
112     struct passwd *temp;
113     temp=getpwuid(geteuid());
114     idmessage(messagedest, ppsz_messages,
115 	      "\n\nNo cdrom drives accessible to %s found.\n",
116 	      temp->pw_name);
117 #else
118     idmessage(messagedest, ppsz_messages,
119 	      "\n\nNo cdrom drives accessible found.\n", NULL);
120 #endif
121   }
122   return(NULL);
123 }
124 
125 #ifdef DEVICE_IN_FILESYSTEM
126 static char *
test_resolve_symlink(const char * file,int messagedest,char ** ppsz_messages)127 test_resolve_symlink(const char *file, int messagedest, char **ppsz_messages)
128 {
129   char resolved[PATH_MAX];
130   struct stat st;
131   if (lstat(file,&st)){
132     idperror(messagedest, ppsz_messages, "\t\tCould not stat %s",file);
133     return(NULL);
134   }
135 
136   if (cdio_realpath(file,resolved))
137     return(strdup(resolved));
138 
139   idperror(messagedest, ppsz_messages, "\t\tCould not resolve symlink %s",
140 	   file);
141   return(NULL);
142 }
143 #endif
144 
145 /** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL
146     if there was an error.
147     @see cdio_cddap_identify_cdio
148  */
149 cdrom_drive_t *
cdio_cddap_identify(const char * psz_dev,int messagedest,char ** ppsz_messages)150 cdio_cddap_identify(const char *psz_dev, int messagedest,
151 char **ppsz_messages)
152 {
153   CdIo_t *p_cdio = NULL;
154 
155   if (psz_dev)
156     idmessage(messagedest, ppsz_messages, "Checking %s for cdrom...",
157 	      psz_dev);
158   else
159     idmessage(messagedest, ppsz_messages, "Checking for cdrom...", NULL);
160 
161 #ifdef DEVICE_IN_FILESYSTEM
162   if (psz_dev) {
163     char *psz_device = test_resolve_symlink(psz_dev, messagedest,
164 					    ppsz_messages);
165     if ( psz_device ) {
166       cdrom_drive_t *d=NULL;
167       p_cdio = cdio_open(psz_device, DRIVER_UNKNOWN);
168       d = cdda_identify_device_cdio(p_cdio, psz_device, messagedest,
169 				    ppsz_messages);
170       free(psz_device);
171       return d;
172     }
173   }
174 #endif
175 
176   p_cdio = cdio_open(psz_dev, DRIVER_UNKNOWN);
177   if (p_cdio) {
178     if (!psz_dev) {
179       psz_dev = cdio_get_arg(p_cdio, "source");
180     }
181     return cdda_identify_device_cdio(p_cdio, psz_dev, messagedest,
182 				     ppsz_messages);
183   }
184   return NULL;
185 }
186 
187 /** Returns a paranoia CD-ROM drive object with a CD-DA in it or NULL
188     if there was an error.  In contrast to cdio_cddap_identify, we
189     start out with an initialized p_cdio object. For example you may
190     have used that for other purposes such as to get CDDB/CD-Text
191     information.  @see cdio_cddap_identify
192  */
193 cdrom_drive_t *
cdio_cddap_identify_cdio(CdIo_t * p_cdio,int messagedest,char ** ppsz_messages)194 cdio_cddap_identify_cdio(CdIo_t *p_cdio, int messagedest, char **ppsz_messages)
195 {
196   if (!p_cdio) return NULL;
197   {
198     const char *psz_device = cdio_get_arg(p_cdio, "source");
199     idmessage(messagedest, ppsz_messages, "Checking %s for cdrom...",
200 	      psz_device);
201     return cdda_identify_device_cdio(p_cdio, psz_device, messagedest,
202 				     ppsz_messages);
203   }
204 
205 }
206 
207 static cdrom_drive_t *
cdda_identify_device_cdio(CdIo_t * p_cdio,const char * psz_device,int messagedest,char ** ppsz_messages)208 cdda_identify_device_cdio(CdIo_t *p_cdio, const char *psz_device,
209 			  int messagedest, char **ppsz_messages)
210 {
211   cdrom_drive_t *d=NULL;
212   int drive_type = 0;
213   char *description=NULL;
214 #ifdef HAVE_LINUX_MAJOR_H
215   struct stat st;
216 #endif
217 
218   if (!p_cdio) {
219     idperror(messagedest, ppsz_messages, "\t\tUnable to open %s", psz_device);
220     return NULL;
221   }
222 
223 #ifdef HAVE_LINUX_MAJOR_H
224   if ( 0 == stat(psz_device, &st) ) {
225     if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
226       drive_type=(int)(st.st_rdev>>8);
227       switch (drive_type) {
228       case IDE0_MAJOR:
229       case IDE1_MAJOR:
230       case IDE2_MAJOR:
231       case IDE3_MAJOR:
232 	/* Yay, ATAPI... */
233 	description=strdup("ATAPI compatible ");
234 	break;
235       case CDU31A_CDROM_MAJOR:
236 	/* major indicates this is a cdrom; no ping necessary. */
237 	description=strdup("Sony CDU31A or compatible");
238 	break;
239       case CDU535_CDROM_MAJOR:
240 	/* major indicates this is a cdrom; no ping necessary. */
241 	description=strdup("Sony CDU535 or compatible");
242 	break;
243 
244       case MATSUSHITA_CDROM_MAJOR:
245       case MATSUSHITA_CDROM2_MAJOR:
246       case MATSUSHITA_CDROM3_MAJOR:
247       case MATSUSHITA_CDROM4_MAJOR:
248 	/* major indicates this is a cdrom; no ping necessary. */
249 	description=strdup("non-ATAPI IDE-style Matsushita/Panasonic CR-5xx or compatible");
250 	break;
251       case SANYO_CDROM_MAJOR:
252 	description=strdup("Sanyo proprietary or compatible: NOT CDDA CAPABLE");
253 	break;
254       case MITSUMI_CDROM_MAJOR:
255       case MITSUMI_X_CDROM_MAJOR:
256 	description=strdup("Mitsumi proprietary or compatible: NOT CDDA CAPABLE");
257 	break;
258       case OPTICS_CDROM_MAJOR:
259 	description=strdup("Optics Dolphin or compatible: NOT CDDA CAPABLE");
260 	break;
261       case AZTECH_CDROM_MAJOR:
262 	description=strdup("Aztech proprietary or compatible: NOT CDDA CAPABLE");
263 	break;
264       case GOLDSTAR_CDROM_MAJOR:
265 	description=strdup("Goldstar proprietary: NOT CDDA CAPABLE");
266 	break;
267       case CM206_CDROM_MAJOR:
268 	description=strdup("Philips/LMS CM206 proprietary: NOT CDDA CAPABLE");
269 	break;
270 
271       case SCSI_CDROM_MAJOR:
272       case SCSI_GENERIC_MAJOR:
273 	/* Nope nope nope */
274 	description=strdup("SCSI CD-ROM");
275 	break;
276       default:
277 	/* What the hell is this? */
278 	idmessage(messagedest, ppsz_messages,
279 		  "\t\t%s is not a cooked ioctl CDROM.",
280 		  psz_device);
281 	return(NULL);
282       }
283     }
284   }
285 #endif /*HAVE_LINUX_MAJOR_H*/
286 
287   /* Minimum init */
288 
289   d=calloc(1, sizeof(cdrom_drive_t));
290   d->p_cdio           = p_cdio;
291   d->cdda_device_name = strdup(psz_device);
292   d->drive_type       = drive_type;
293   d->bigendianp       = -1; /* We don't know yet... */
294   d->nsectors         = -1; /* We don't know yet... */
295   d->messagedest      = messagedest;
296   d->b_swap_bytes     = true;
297 
298   {
299     cdio_hwinfo_t hw_info = {
300       "UNKNOWN", "Unknown model", "????"
301     };
302 
303     if ( mmc_get_hwinfo( p_cdio, &hw_info ) ) {
304       unsigned int i_len = strlen(hw_info.psz_vendor)
305 	+ strlen(hw_info.psz_model)
306 	+ strlen(hw_info.psz_revision) + 5;
307 
308       if (description) {
309 	i_len += strlen(description);
310 	d->drive_model=malloc( i_len );
311 	snprintf( d->drive_model, i_len, "%s %s %s %s",
312 		  hw_info.psz_vendor, hw_info.psz_model, hw_info.psz_revision,
313 		  description );
314       } else {
315 	d->drive_model=malloc( i_len );
316 	snprintf( d->drive_model, i_len, "%s %s %s",
317 		  hw_info.psz_vendor, hw_info.psz_model, hw_info.psz_revision
318 		  );
319       }
320       idmessage(messagedest, ppsz_messages, "\t\tCDROM sensed: %s\n",
321 		d->drive_model);
322     }
323   }
324 
325   if (description)
326     free(description);
327 
328   return(d);
329 }
330