1 | /* |
---|
2 | * Copyright (c) 1992-1998 Michael A. Cooper. |
---|
3 | * This software may be freely used and distributed provided it is not |
---|
4 | * sold for profit or used in part or in whole for commercial gain |
---|
5 | * without prior written agreement, and the author is credited |
---|
6 | * appropriately. |
---|
7 | */ |
---|
8 | |
---|
9 | #ifndef lint |
---|
10 | static char *RCSid = "$Revision: 1.1.1.3 $"; |
---|
11 | #endif |
---|
12 | |
---|
13 | /* |
---|
14 | * SunOS 5.x specific routines |
---|
15 | */ |
---|
16 | |
---|
17 | #include "defs.h" |
---|
18 | #include <dirent.h> |
---|
19 | #include <sys/stat.h> |
---|
20 | #include <sys/cdio.h> |
---|
21 | #include <sys/fdio.h> |
---|
22 | #if defined(HAVE_VOLMGT) |
---|
23 | #include <volmgt.h> |
---|
24 | #endif /* HAVE_VOLMGT */ |
---|
25 | |
---|
26 | /* |
---|
27 | * CPU (model) symbol |
---|
28 | */ |
---|
29 | char CpuSYM[] = "_cputype"; |
---|
30 | |
---|
31 | static char Buff[BUFSIZ]; |
---|
32 | DevInfo_t *DevInfo; |
---|
33 | extern char RomVecSYM[]; |
---|
34 | struct stat StatBuf; |
---|
35 | |
---|
36 | /* |
---|
37 | * Get device file list. |
---|
38 | * Finds a list of match device files. |
---|
39 | * File list is set to ArgvPtr. |
---|
40 | * Returns number of files found (in ArgvPtr). |
---|
41 | * |
---|
42 | * If DevDefine->File is set, use that to find file name. |
---|
43 | * Otherwise, scan DevDir looking for a device file that matches |
---|
44 | * DevData->DevNum. |
---|
45 | */ |
---|
46 | static int GetDeviceFile(ProbeData, DevDir, ArgvPtr) |
---|
47 | ProbeData_t *ProbeData; |
---|
48 | char *DevDir; |
---|
49 | char ***ArgvPtr; |
---|
50 | { |
---|
51 | static DIR *DirPtr = NULL; |
---|
52 | struct dirent *DirEnt; |
---|
53 | static char PathBuff[MAXPATHLEN]; |
---|
54 | char *Path = PathBuff; |
---|
55 | static char LastDir[MAXPATHLEN]; |
---|
56 | register int i; |
---|
57 | int Len; |
---|
58 | int Argc; |
---|
59 | char **Argv = NULL; |
---|
60 | static char **FileArgv = NULL; |
---|
61 | static int FileArgc = 0; |
---|
62 | char *File; |
---|
63 | DevData_t *DevData; |
---|
64 | DevDefine_t *DevDefine; |
---|
65 | |
---|
66 | if (!ProbeData) |
---|
67 | return(0); |
---|
68 | DevData = ProbeData->DevData; |
---|
69 | DevDefine = ProbeData->DevDefine; |
---|
70 | |
---|
71 | /* Don't bother trying if the devnum is 0 */ |
---|
72 | if (DevData->DevNum <= 0) |
---|
73 | return(0); |
---|
74 | |
---|
75 | if (FileArgv && FileArgc > 0) { |
---|
76 | DestroyArgv(&FileArgv, FileArgc); |
---|
77 | FileArgv = NULL; |
---|
78 | FileArgc = 0; |
---|
79 | } |
---|
80 | |
---|
81 | if (DevDefine->File) { |
---|
82 | /* |
---|
83 | * DevDefine->File is a space seperated list of file names. |
---|
84 | * If a '%' is found, then the unit number is inserted at |
---|
85 | * that point. |
---|
86 | */ |
---|
87 | Argc = StrToArgv(DevDefine->File, " ", &Argv, NULL, 0); |
---|
88 | if (Argc <= 0) |
---|
89 | return(0); |
---|
90 | FileArgv = (char **) xcalloc(Argc, sizeof(char **)); |
---|
91 | for (i = 0; i < Argc; ++i) { |
---|
92 | File = Argv[i]; |
---|
93 | /* Allow NULL members of Argv */ |
---|
94 | if (!File || !File[0]) |
---|
95 | continue; |
---|
96 | /* |
---|
97 | * If File doesn't start with '/', then insert DevDir. |
---|
98 | * If File contains '%', then insert the device number. |
---|
99 | */ |
---|
100 | PathBuff[0] = CNULL; |
---|
101 | if (File[0] != '/') |
---|
102 | (void) snprintf(PathBuff, sizeof(PathBuff), "%s/", DevDir); |
---|
103 | Path = PathBuff + (Len = strlen(PathBuff)); |
---|
104 | |
---|
105 | if (strchr(File, '%') && DevData->DevUnit >= 0) |
---|
106 | (void) snprintf(Path, sizeof(PathBuff)-Len, |
---|
107 | File, DevData->DevUnit); |
---|
108 | else |
---|
109 | (void) strcat(Path, File); |
---|
110 | |
---|
111 | if (Path[0]) { |
---|
112 | if (stat(Path, &StatBuf) == 0) |
---|
113 | FileArgv[FileArgc++] = strdup(Path); |
---|
114 | else { |
---|
115 | SImsg(SIM_GERR, "Stat of %s failed: %s.", Path, SYSERR); |
---|
116 | Argv[i] = NULL; |
---|
117 | } |
---|
118 | } else |
---|
119 | SImsg(SIM_DBG, "Convert pathname failed: %s", File); |
---|
120 | } |
---|
121 | if (Argc) |
---|
122 | DestroyArgv(&Argv, Argc); |
---|
123 | } else { |
---|
124 | if (!DirPtr || !EQ(DevDir, LastDir)) { |
---|
125 | if (!(DirPtr = opendir(DevDir))) { |
---|
126 | SImsg(SIM_GERR, "Cannot open directory \"%s\": %s", |
---|
127 | DevDir, SYSERR); |
---|
128 | return(-1); |
---|
129 | } |
---|
130 | (void) strcpy(LastDir, DevDir); |
---|
131 | } else |
---|
132 | rewinddir(DirPtr); |
---|
133 | |
---|
134 | Path = PathBuff; |
---|
135 | while (DirEnt = readdir(DirPtr)) { |
---|
136 | if (EQ(DirEnt->d_name, ".") || EQ(DirEnt->d_name, "..")) |
---|
137 | continue; |
---|
138 | |
---|
139 | (void) snprintf(Path, sizeof(PathBuff), "%s/%s", |
---|
140 | DevDir, DirEnt->d_name); |
---|
141 | |
---|
142 | if (stat(Path, &StatBuf) < 0) { |
---|
143 | SImsg(SIM_GERR, "Stat of %s failed: %s.", Path, SYSERR); |
---|
144 | continue; |
---|
145 | } |
---|
146 | |
---|
147 | #if defined(DEBUG_VERBOSE) |
---|
148 | SImsg(SIM_DBG, "GetDeviceFile dev '%s' (%d) got '%s' (%d)", |
---|
149 | DevData->DevName, DevData->DevNum, |
---|
150 | Path, StatBuf.st_rdev); |
---|
151 | #endif /* DEBUG_VERBOSE */ |
---|
152 | |
---|
153 | if (StatBuf.st_rdev == DevData->DevNum) { |
---|
154 | FileArgv = (char **) xcalloc(2, sizeof(char *)); |
---|
155 | FileArgv[FileArgc++] = strdup(Path); |
---|
156 | break; |
---|
157 | } |
---|
158 | } |
---|
159 | } |
---|
160 | |
---|
161 | if (FileArgc) |
---|
162 | *ArgvPtr = FileArgv; |
---|
163 | |
---|
164 | return(FileArgc); |
---|
165 | } |
---|
166 | |
---|
167 | /* |
---|
168 | * Create a tape device |
---|
169 | */ |
---|
170 | static DevInfo_t *CreateTapeDrive(ProbeData, DevName, TapeName) |
---|
171 | ProbeData_t *ProbeData; |
---|
172 | char *DevName; |
---|
173 | char *TapeName; |
---|
174 | { |
---|
175 | DevInfo_t *DevInfo; |
---|
176 | |
---|
177 | ProbeData->DevName = DevName; |
---|
178 | DevInfo = DeviceCreate(ProbeData); |
---|
179 | if (!EQ(DevName, TapeName)) |
---|
180 | DevInfo->AltName = strdup(TapeName); |
---|
181 | DevInfo->Type = DT_TAPEDRIVE; |
---|
182 | |
---|
183 | return(DevInfo); |
---|
184 | } |
---|
185 | |
---|
186 | /* |
---|
187 | * Probe a Tape Drive. |
---|
188 | */ |
---|
189 | extern DevInfo_t *ProbeTapeDrive(ProbeData) |
---|
190 | ProbeData_t *ProbeData; |
---|
191 | { |
---|
192 | DevInfo_t *DevInfo; |
---|
193 | char *DevFile; |
---|
194 | char *DevName; |
---|
195 | char *Model = NULL; |
---|
196 | char **FileList; |
---|
197 | int FileNum; |
---|
198 | static char DevFilen[MAXPATHLEN]; |
---|
199 | struct mtget mtget; |
---|
200 | register char *cp; |
---|
201 | int fd; |
---|
202 | DevDesc_t *ScsiDevDesc; |
---|
203 | char *TapeName; |
---|
204 | DevData_t *DevData; |
---|
205 | DevDefine_t *DevDefine; |
---|
206 | |
---|
207 | TapeName = ProbeData->DevName; |
---|
208 | DevData = ProbeData->DevData; |
---|
209 | DevDefine = ProbeData->DevDefine; |
---|
210 | |
---|
211 | FileNum = GetDeviceFile(ProbeData, _PATH_DEV_RMT, &FileList); |
---|
212 | if (FileNum < 1) { |
---|
213 | SImsg(SIM_GERR, "Cannot find tape device for <%s>", TapeName); |
---|
214 | return((DevInfo_t *)NULL); |
---|
215 | } |
---|
216 | /* Only bother with the first file name */ |
---|
217 | DevFile = FileList[0]; |
---|
218 | |
---|
219 | /* |
---|
220 | * Use DevFile to get name of device. |
---|
221 | * Takes something like "/dev/rmt/0l" and |
---|
222 | * turns it into "rmt/0". |
---|
223 | */ |
---|
224 | cp = DevFile; |
---|
225 | /* Skip over "/dev/" */ |
---|
226 | if ((int)strlen(DevFile) > 5 && EQN(DevFile, "/dev/", 4)) |
---|
227 | cp += 5; |
---|
228 | DevName = cp; |
---|
229 | if (cp = strrchr(DevFile, '/')) { |
---|
230 | ++cp; |
---|
231 | while (isdigit(*++cp)); |
---|
232 | if (*cp && !isdigit(*cp)) |
---|
233 | *cp = CNULL; |
---|
234 | } |
---|
235 | |
---|
236 | /* Use the no rewind file */ |
---|
237 | (void) snprintf(DevFilen, sizeof(DevFilen), "%sn", DevFile); |
---|
238 | |
---|
239 | /* |
---|
240 | * The O_NDELAY flag will allow the device to be opened even if no |
---|
241 | * media is loaded. |
---|
242 | */ |
---|
243 | if ((fd = open(DevFilen, O_RDONLY|O_NDELAY)) < 0) { |
---|
244 | SImsg(SIM_GERR, "%s Cannot open for read: %s.", DevFilen, SYSERR); |
---|
245 | |
---|
246 | /* |
---|
247 | * If we know for sure this drive is present and we |
---|
248 | * know something about it, then create a minimal device. |
---|
249 | */ |
---|
250 | if ((DevDefine->Model || DevDefine->Desc) && |
---|
251 | FLAGS_ON(DevData->Flags, DD_IS_ALIVE)) { |
---|
252 | DevInfo = CreateTapeDrive(ProbeData, DevName, TapeName); |
---|
253 | return(ProbeData->RetDevInfo = DevInfo); |
---|
254 | } else |
---|
255 | return((DevInfo_t *) NULL); |
---|
256 | } |
---|
257 | |
---|
258 | if (ioctl(fd, MTIOCGET, &mtget) != 0) { |
---|
259 | SImsg(SIM_GERR, "%s: Cannot extract tape status: %s.", |
---|
260 | DevName, SYSERR); |
---|
261 | return((DevInfo_t *) NULL); |
---|
262 | } |
---|
263 | |
---|
264 | cp = GetTapeModel(mtget.mt_type); |
---|
265 | if (cp) |
---|
266 | Model = strdup(cp); |
---|
267 | else |
---|
268 | Model = "unknown"; |
---|
269 | |
---|
270 | /* |
---|
271 | * Set device info |
---|
272 | */ |
---|
273 | DevInfo = CreateTapeDrive(ProbeData, DevName, TapeName); |
---|
274 | if (Model) |
---|
275 | DevInfo->Model = Model; |
---|
276 | else |
---|
277 | DevInfo->Model = DevDefine->Model; |
---|
278 | DevInfo->Master = MkMasterFromDevData(DevData); |
---|
279 | |
---|
280 | (void) ScsiQuery(DevInfo, DevFilen, fd, TRUE); |
---|
281 | |
---|
282 | (void) close(fd); |
---|
283 | |
---|
284 | return(ProbeData->RetDevInfo = DevInfo); |
---|
285 | } |
---|
286 | |
---|
287 | /* |
---|
288 | * Get SVR4 style partition information |
---|
289 | */ |
---|
290 | DKvtoc *GETvtoc(fd, DevFile) |
---|
291 | int fd; |
---|
292 | char *DevFile; |
---|
293 | { |
---|
294 | static DKvtoc vtoc; |
---|
295 | |
---|
296 | if (ioctl(fd, DKIOCGVTOC, &vtoc) < 0) { |
---|
297 | SImsg(SIM_GERR, "%s: DKIOCGVTOC failed: %s.", DevFile, SYSERR); |
---|
298 | return((DKvtoc *)NULL); |
---|
299 | } |
---|
300 | |
---|
301 | return(&vtoc); |
---|
302 | } |
---|
303 | |
---|
304 | /* |
---|
305 | * Get controller information |
---|
306 | */ |
---|
307 | DKcinfo *GETdk_cinfo(fd, DevFile) |
---|
308 | int fd; |
---|
309 | char *DevFile; |
---|
310 | { |
---|
311 | static DKcinfo dk_cinfo; |
---|
312 | |
---|
313 | if (ioctl(fd, DKIOCINFO, &dk_cinfo) < 0) { |
---|
314 | SImsg(SIM_GERR, "%s: DKIOCINFO failed: %s.", DevFile, SYSERR); |
---|
315 | return((DKcinfo *)NULL); |
---|
316 | } |
---|
317 | |
---|
318 | return(&dk_cinfo); |
---|
319 | } |
---|
320 | |
---|
321 | /* |
---|
322 | * Get disk geometry |
---|
323 | */ |
---|
324 | DKgeom *GETdk_geom(fd, DevFile) |
---|
325 | int fd; |
---|
326 | char *DevFile; |
---|
327 | { |
---|
328 | static DKgeom dk_geom; |
---|
329 | |
---|
330 | if (ioctl(fd, DKIOCGGEOM, &dk_geom) < 0) { |
---|
331 | SImsg(SIM_GERR, "%s: DKIOCGGEOM failed: %s.", DevFile, SYSERR); |
---|
332 | return((DKgeom *)NULL); |
---|
333 | } |
---|
334 | |
---|
335 | return(&dk_geom); |
---|
336 | } |
---|
337 | |
---|
338 | #if defined(HAVE_HDIO) |
---|
339 | /* |
---|
340 | * Get disk type structure. |
---|
341 | */ |
---|
342 | static DKtype *GETdk_type(fd, file) |
---|
343 | int fd; |
---|
344 | char *file; |
---|
345 | { |
---|
346 | static DKtype dk_type; |
---|
347 | |
---|
348 | if (ioctl(fd, HDKIOCGTYPE, &dk_type) < 0) { |
---|
349 | if (errno != ENOTTY) |
---|
350 | SImsg(SIM_GERR, "%s: DKIOCGTYPE failed: %s.", file, SYSERR); |
---|
351 | return(NULL); |
---|
352 | } |
---|
353 | |
---|
354 | return(&dk_type); |
---|
355 | } |
---|
356 | #endif /* HAVE_HDIO */ |
---|
357 | |
---|
358 | /* |
---|
359 | * Find mount point for a given device |
---|
360 | */ |
---|
361 | static char *GetMountInfo(DevName, PartName) |
---|
362 | char *DevName; |
---|
363 | char *PartName; |
---|
364 | { |
---|
365 | static FILE *mntFilePtr = NULL; |
---|
366 | static FILE *vfstabFilePtr = NULL; |
---|
367 | struct mnttab MntTab; |
---|
368 | struct vfstab vfstab; |
---|
369 | static char Name[MAXPATHLEN]; |
---|
370 | static char Path[MAXPATHLEN]; |
---|
371 | register char *cp; |
---|
372 | |
---|
373 | if (!DevName || !PartName) |
---|
374 | return((char *) NULL); |
---|
375 | |
---|
376 | (void) snprintf(Name, sizeof(Name), "%s%s", DevName, PartName); |
---|
377 | |
---|
378 | /* |
---|
379 | * First try the current mount table |
---|
380 | */ |
---|
381 | if (!mntFilePtr) { |
---|
382 | if ((mntFilePtr = fopen(MNTTAB, "r")) == NULL) { |
---|
383 | SImsg(SIM_GERR, "%s: Cannot open for reading: %s.", |
---|
384 | MNTTAB, SYSERR); |
---|
385 | return(NULL); |
---|
386 | } |
---|
387 | } else |
---|
388 | rewind(mntFilePtr); |
---|
389 | |
---|
390 | while (getmntent(mntFilePtr, &MntTab) == 0) { |
---|
391 | if (!MntTab.mnt_special) |
---|
392 | continue; |
---|
393 | if (cp = strrchr(MntTab.mnt_special, '/')) |
---|
394 | ++cp; |
---|
395 | else |
---|
396 | cp = MntTab.mnt_special; |
---|
397 | if (EQ(cp, Name)) |
---|
398 | return(strdup(MntTab.mnt_mountp)); |
---|
399 | } |
---|
400 | |
---|
401 | /* |
---|
402 | * Now try the static mount table, which may not reflect current reality. |
---|
403 | */ |
---|
404 | if (!vfstabFilePtr) { |
---|
405 | if ((vfstabFilePtr = fopen(VFSTAB, "r")) == NULL) { |
---|
406 | SImsg(SIM_GERR, "%s: Cannot open for reading: %s.", |
---|
407 | VFSTAB, SYSERR); |
---|
408 | return(NULL); |
---|
409 | } |
---|
410 | } else |
---|
411 | rewind(vfstabFilePtr); |
---|
412 | |
---|
413 | (void) snprintf(Path, sizeof(Path), "%s/%s", _PATH_DEV_DSK, Name); |
---|
414 | |
---|
415 | if (getvfsspec(vfstabFilePtr, &vfstab, Path) == 0) { |
---|
416 | if (EQ(vfstab.vfs_fstype, "swap")) |
---|
417 | return("swap"); |
---|
418 | else if (vfstab.vfs_mountp && !EQ(vfstab.vfs_mountp, "-")) |
---|
419 | return(strdup(vfstab.vfs_mountp)); |
---|
420 | } |
---|
421 | |
---|
422 | return((char *) NULL); |
---|
423 | } |
---|
424 | |
---|
425 | /* |
---|
426 | * Translate disk partition information from basic |
---|
427 | * extracted disk info. |
---|
428 | */ |
---|
429 | static DiskPart_t *GetDiskPart(DevName, dk_vtoc) |
---|
430 | char *DevName; |
---|
431 | DKvtoc *dk_vtoc; |
---|
432 | { |
---|
433 | register ushort i; |
---|
434 | static char pname[4]; |
---|
435 | DiskPart_t *Base = NULL; |
---|
436 | DiskPart_t *Last = NULL; |
---|
437 | DiskPart_t *DiskPart = NULL; |
---|
438 | |
---|
439 | if (!DevName || !dk_vtoc) |
---|
440 | return((DiskPart_t *) NULL); |
---|
441 | |
---|
442 | pname[2] = CNULL; |
---|
443 | /* |
---|
444 | * dk_vtoc->v_nparts is always 0 after the first time through |
---|
445 | * here, so we use V_NUMPAR. |
---|
446 | */ |
---|
447 | for (i = 0; i < V_NUMPAR; ++i) { |
---|
448 | /* Skip partitions that have no size */ |
---|
449 | if (!dk_vtoc->v_part[i].p_size) |
---|
450 | continue; |
---|
451 | DiskPart = (DiskPart_t *) xcalloc(1, sizeof(DiskPart_t)); |
---|
452 | (void) snprintf(pname, sizeof(pname), "s%d", i); |
---|
453 | DiskPart->Name = strdup(pname); |
---|
454 | DiskPart->Usage = GetMountInfo(DevName, pname); |
---|
455 | DiskPart->StartSect = (Large_t) dk_vtoc->v_part[i].p_start; |
---|
456 | DiskPart->NumSect = (Large_t) dk_vtoc->v_part[i].p_size; |
---|
457 | |
---|
458 | if (Base) { |
---|
459 | Last->Next = DiskPart; |
---|
460 | Last = DiskPart; |
---|
461 | } else |
---|
462 | Base = Last = DiskPart; |
---|
463 | } |
---|
464 | |
---|
465 | return(Base); |
---|
466 | } |
---|
467 | |
---|
468 | /* |
---|
469 | * Extract disk label |
---|
470 | */ |
---|
471 | static char *GetDiskLabel(dk_vtoc) |
---|
472 | DKvtoc *dk_vtoc; |
---|
473 | { |
---|
474 | register char *cp; |
---|
475 | register char *bp; |
---|
476 | register char *end; |
---|
477 | char *Label = NULL; |
---|
478 | |
---|
479 | if (!dk_vtoc || !dk_vtoc->v_asciilabel) |
---|
480 | return((char *)NULL); |
---|
481 | |
---|
482 | Label = dk_vtoc->v_asciilabel; |
---|
483 | |
---|
484 | /* |
---|
485 | * The label normally has geometry information in it we don't want |
---|
486 | * to see, so we trim out anything starting with " cyl". |
---|
487 | */ |
---|
488 | for (cp = Label; cp && *cp; ++cp) |
---|
489 | if (*cp == ' ' && strncasecmp(cp, " cyl", 4) == 0) { |
---|
490 | *cp = CNULL; |
---|
491 | break; |
---|
492 | } |
---|
493 | |
---|
494 | /* |
---|
495 | * Zap any trailing white space we find |
---|
496 | */ |
---|
497 | bp = Label; |
---|
498 | bp += strlen(Label); |
---|
499 | end = bp; |
---|
500 | for (cp = end - 1; cp > Label && *cp == ' '; --cp); |
---|
501 | if (cp != end - 1 && cp && *++cp == ' ') |
---|
502 | *cp = CNULL; |
---|
503 | |
---|
504 | /* |
---|
505 | * Check what we have so far. |
---|
506 | * "DEFAULT" is sometimes used by Solaris x86. |
---|
507 | */ |
---|
508 | if (!Label || !Label[0] || EQ(Label, "DEFAULT")) |
---|
509 | return((char *) NULL); |
---|
510 | |
---|
511 | return(strdup(Label)); |
---|
512 | } |
---|
513 | |
---|
514 | /* |
---|
515 | * Get the name of the controller for a disk. |
---|
516 | */ |
---|
517 | static char *GetDkCtlrName(dk_cinfo) |
---|
518 | DKcinfo *dk_cinfo; |
---|
519 | { |
---|
520 | if (!dk_cinfo) |
---|
521 | return((char *) NULL); |
---|
522 | |
---|
523 | (void) snprintf(Buff, sizeof(Buff), "%s%d", |
---|
524 | dk_cinfo->dki_cname, dk_cinfo->dki_cnum); |
---|
525 | |
---|
526 | return(strdup(Buff)); |
---|
527 | } |
---|
528 | |
---|
529 | /* |
---|
530 | * Get a disk controller device from disk info. |
---|
531 | */ |
---|
532 | static DevInfo_t *GetDkCtlrDevice(DevData, dk_cinfo) |
---|
533 | DevData_t *DevData; |
---|
534 | DKcinfo *dk_cinfo; |
---|
535 | { |
---|
536 | DevInfo_t *MkMasterFromDevData(); |
---|
537 | DevInfo_t *DiskCtlr; |
---|
538 | |
---|
539 | if (!dk_cinfo) |
---|
540 | return((DevInfo_t *) NULL); |
---|
541 | |
---|
542 | /* |
---|
543 | * Get name of controller from devdata if available |
---|
544 | */ |
---|
545 | if (DevData && DevData->CtlrName) |
---|
546 | DiskCtlr = MkMasterFromDevData(DevData); |
---|
547 | else { |
---|
548 | if ((DiskCtlr = NewDevInfo(NULL)) == NULL) |
---|
549 | return((DevInfo_t *) NULL); |
---|
550 | } |
---|
551 | |
---|
552 | DiskCtlr->Type = DT_DISKCTLR; |
---|
553 | |
---|
554 | /* |
---|
555 | * Get name of controller from devdata if available |
---|
556 | */ |
---|
557 | if (DevData && DevData->CtlrName) |
---|
558 | DiskCtlr = MkMasterFromDevData(DevData); |
---|
559 | |
---|
560 | if (!DiskCtlr->Name) { |
---|
561 | DiskCtlr->Name = GetDkCtlrName(dk_cinfo); |
---|
562 | DiskCtlr->Unit = dk_cinfo->dki_cnum; |
---|
563 | } |
---|
564 | DiskCtlr->Addr = dk_cinfo->dki_addr; |
---|
565 | DiskCtlr->Prio = dk_cinfo->dki_prio; |
---|
566 | DiskCtlr->Vec = dk_cinfo->dki_vec; |
---|
567 | SetDiskCtlrModel(DiskCtlr, dk_cinfo->dki_ctype); |
---|
568 | |
---|
569 | return(DiskCtlr); |
---|
570 | } |
---|
571 | |
---|
572 | /* |
---|
573 | * Convert all we've learned about a disk to a DevInfo_t. |
---|
574 | */ |
---|
575 | static DevInfo_t *CreateDiskDrive(ProbeData, DevName, |
---|
576 | dk_vtoc, dk_cinfo, dk_geom, dk_type) |
---|
577 | ProbeData_t *ProbeData; |
---|
578 | char *DevName; |
---|
579 | DKvtoc *dk_vtoc; |
---|
580 | DKcinfo *dk_cinfo; |
---|
581 | DKgeom *dk_geom; |
---|
582 | DKtype *dk_type; |
---|
583 | { |
---|
584 | DevInfo_t *DevInfo; |
---|
585 | DevInfo_t *DiskCtlr = NULL; |
---|
586 | DiskDriveData_t *DiskDriveData = NULL; |
---|
587 | DiskDrive_t *DiskDrive = NULL; |
---|
588 | DevData_t *DevData; |
---|
589 | static DevInfo_t ScsiDevInfo; |
---|
590 | int GotScsi = FALSE; |
---|
591 | int DevFD; |
---|
592 | |
---|
593 | if (!ProbeData) |
---|
594 | return((DevInfo_t *) NULL); |
---|
595 | |
---|
596 | DevFD = ProbeData->FileDesc; |
---|
597 | DevData = ProbeData->DevData; |
---|
598 | |
---|
599 | ProbeData->DevName = DevName; |
---|
600 | if (!(DevInfo = DeviceCreate(ProbeData))) { |
---|
601 | SImsg(SIM_GERR, "Cannot create new device entry."); |
---|
602 | return((DevInfo_t *) NULL); |
---|
603 | } |
---|
604 | |
---|
605 | /* |
---|
606 | * We call ScsiQuery() here because it needs a DevInfo to operate on. |
---|
607 | */ |
---|
608 | if (ScsiQuery(DevInfo, ProbeData->DevFile, DevFD, TRUE) == 0) |
---|
609 | GotScsi = TRUE; |
---|
610 | |
---|
611 | if (dk_vtoc == NULL) { |
---|
612 | SImsg(SIM_GERR, "%s: No table of contents found on disk.", DevName); |
---|
613 | if (!GotScsi) |
---|
614 | return((DevInfo_t *) NULL); |
---|
615 | } |
---|
616 | |
---|
617 | if (DevInfo->DevSpec) |
---|
618 | DiskDriveData = (DiskDriveData_t *) DevInfo->DevSpec; |
---|
619 | else { |
---|
620 | if ((DiskDriveData = NewDiskDriveData(NULL)) == NULL) { |
---|
621 | SImsg(SIM_GERR, "Cannot create new DiskDriveData entry."); |
---|
622 | return((DevInfo_t *) NULL); |
---|
623 | } |
---|
624 | DevInfo->DevSpec = (void *) DiskDriveData; |
---|
625 | } |
---|
626 | |
---|
627 | if ((DiskDrive = NewDiskDrive(NULL)) == NULL) { |
---|
628 | SImsg(SIM_GERR, "Cannot create new DiskDrive entry."); |
---|
629 | return((DevInfo_t *) NULL); |
---|
630 | } |
---|
631 | /* |
---|
632 | * DiskDrive is the OS DiskDrive data |
---|
633 | */ |
---|
634 | DiskDriveData->OSdata = DiskDrive; |
---|
635 | |
---|
636 | (void) snprintf(Buff, sizeof(Buff), "%s%d", |
---|
637 | DevData->DevName, DevData->DevUnit); |
---|
638 | DevInfo->AltName = strdup(Buff); |
---|
639 | DevInfo->Type = DT_DISKDRIVE; |
---|
640 | |
---|
641 | if (dk_vtoc && dk_vtoc->v_sanity == VTOC_SANE) { |
---|
642 | /* |
---|
643 | * Only read partition info we we're going to print it later. |
---|
644 | */ |
---|
645 | if (VL_ALL) |
---|
646 | DiskDrive->DiskPart = GetDiskPart(DevName, dk_vtoc); |
---|
647 | if (!DiskDrive->Label) |
---|
648 | DiskDrive->Label = GetDiskLabel(dk_vtoc); |
---|
649 | DiskDrive->SecSize = dk_vtoc->v_sectorsz; |
---|
650 | if (dk_vtoc->v_volume[0]) { |
---|
651 | (void) snprintf(Buff, sizeof(Buff), "\"%.*s\"", |
---|
652 | sizeof(dk_vtoc->v_volume), dk_vtoc->v_volume); |
---|
653 | AddDevDesc(DevInfo, Buff, "Volume Name", DA_APPEND); |
---|
654 | } |
---|
655 | } |
---|
656 | if (!DevInfo->Model) |
---|
657 | DevInfo->Model = DiskDrive->Label; |
---|
658 | |
---|
659 | if (dk_cinfo) { |
---|
660 | DiskDrive->Unit = dk_cinfo->dki_unit; |
---|
661 | DiskDrive->Slave = dk_cinfo->dki_slave;; |
---|
662 | } |
---|
663 | if (dk_geom) { |
---|
664 | DiskDrive->DataCyl = dk_geom->dkg_ncyl; |
---|
665 | DiskDrive->PhyCyl = dk_geom->dkg_pcyl; |
---|
666 | DiskDrive->AltCyl = dk_geom->dkg_acyl; |
---|
667 | DiskDrive->Tracks = dk_geom->dkg_nhead; |
---|
668 | DiskDrive->Sect = dk_geom->dkg_nsect; |
---|
669 | DiskDrive->APC = dk_geom->dkg_apc; |
---|
670 | DiskDrive->RPM = dk_geom->dkg_rpm; |
---|
671 | DiskDrive->IntrLv = dk_geom->dkg_intrlv; |
---|
672 | } |
---|
673 | #if defined(HAVE_HDIO) |
---|
674 | if (dk_type) { |
---|
675 | DiskDrive->PhySect = dk_type->hdkt_hsect; |
---|
676 | DiskDrive->PROMRev = dk_type->hdkt_promrev; |
---|
677 | } |
---|
678 | #endif /* HAVE_HDIO */ |
---|
679 | |
---|
680 | if (DiskCtlr = GetDkCtlrDevice(DevData, dk_cinfo)) |
---|
681 | DevInfo->Master = DiskCtlr; |
---|
682 | |
---|
683 | #if defined(i386) |
---|
684 | (void) DosPartGet(DevInfo); |
---|
685 | #endif |
---|
686 | |
---|
687 | return(ProbeData->RetDevInfo = DevInfo); |
---|
688 | } |
---|
689 | |
---|
690 | /* |
---|
691 | * Create a base DiskDrive device. |
---|
692 | */ |
---|
693 | static DevInfo_t *CreateBaseDiskDrive(ProbeData, DiskName) |
---|
694 | ProbeData_t *ProbeData; |
---|
695 | char *DiskName; |
---|
696 | { |
---|
697 | DevInfo_t *DevInfo; |
---|
698 | DevData_t *DevData; |
---|
699 | DevDefine_t *DevDefine; |
---|
700 | char *DevName; |
---|
701 | char *AltName = NULL; |
---|
702 | |
---|
703 | DevName = ProbeData->DevName; |
---|
704 | DevData = ProbeData->DevData; |
---|
705 | DevDefine = ProbeData->DevDefine; |
---|
706 | |
---|
707 | if (DiskName) |
---|
708 | ProbeData->DevName = DiskName; |
---|
709 | |
---|
710 | DevInfo = DeviceCreate(ProbeData); |
---|
711 | if (!DevInfo) |
---|
712 | return((DevInfo_t *) NULL); |
---|
713 | |
---|
714 | /* |
---|
715 | * See if there's a good alternative name we can set. |
---|
716 | */ |
---|
717 | if (DevData) |
---|
718 | AltName = MkDevName(DevData->DevName, DevData->DevUnit, |
---|
719 | DevDefine->Type, DevDefine->Flags); |
---|
720 | else if (!EQ(DiskName, DevName)) |
---|
721 | AltName = DiskName; |
---|
722 | if (AltName && !EQ(DevInfo->Name, AltName)) |
---|
723 | DevInfo->AltName = strdup(AltName); |
---|
724 | |
---|
725 | return(ProbeData->RetDevInfo = DevInfo); |
---|
726 | } |
---|
727 | |
---|
728 | /* |
---|
729 | * Create CDROM device |
---|
730 | */ |
---|
731 | static DevInfo_t *CreateCDROM(ProbeData, DevName, CDspeed) |
---|
732 | ProbeData_t *ProbeData; |
---|
733 | char *DevName; |
---|
734 | int CDspeed; |
---|
735 | { |
---|
736 | DevInfo_t *DevInfo; |
---|
737 | Define_t *Def; |
---|
738 | |
---|
739 | DevInfo = CreateBaseDiskDrive(ProbeData, DevName); |
---|
740 | |
---|
741 | Def = DefGet(DL_CDSPEED, NULL, CDspeed, 0); |
---|
742 | if (Def) |
---|
743 | DevInfo->ModelDesc = Def->ValStr1; |
---|
744 | else |
---|
745 | SImsg(SIM_UNKN, "Unknown CDROM Speed: 0x%x", CDspeed); |
---|
746 | |
---|
747 | return(DevInfo); |
---|
748 | } |
---|
749 | |
---|
750 | #if defined(HAVE_VOLMGT) |
---|
751 | /* |
---|
752 | * Use the Solaris Volume Management API to acquire the device. |
---|
753 | * NOTE: Most of this code doesn't do us much good right now because |
---|
754 | * not all ioctl()'s are passed through volfs(7) to the underlying driver. |
---|
755 | * In particuliar CDROMGDRVSPEED doesn't work as of at least SunOS 5.6. |
---|
756 | * |
---|
757 | * Returns the pathname to the acquire device on success. |
---|
758 | * Returns NULL on failure. |
---|
759 | */ |
---|
760 | static char *VolMgtAcquire(ProbeData) |
---|
761 | ProbeData_t *ProbeData; |
---|
762 | { |
---|
763 | const char *VolRoot = NULL; |
---|
764 | char *VolPath = NULL; |
---|
765 | char *ErrPtr = NULL; |
---|
766 | pid_t ProcID = 0; |
---|
767 | char *cp; |
---|
768 | |
---|
769 | if (!(VolRoot = volmgt_root())) { |
---|
770 | SImsg(SIM_GERR, "Get volume manager root failed: %s", SYSERR); |
---|
771 | return((char *) NULL); |
---|
772 | } |
---|
773 | |
---|
774 | VolPath = (char *) xmalloc(strlen(VolRoot) + |
---|
775 | strlen(ProbeData->DevFile) + 1); |
---|
776 | |
---|
777 | (void) snprintf(VolPath, sizeof(VolPath), "%s%s", |
---|
778 | VolRoot, ProbeData->DevFile); |
---|
779 | /* |
---|
780 | * VolPath should be something like "/vol/dev/rdsk/c0t2d0s2" for a CD. |
---|
781 | * If so, remove the 'sX' from VolPath. |
---|
782 | */ |
---|
783 | if (EQN(VolPath + strlen(VolRoot), "/dev/rdsk/c", 11)) |
---|
784 | if (cp = strrchr(VolPath, 's')) |
---|
785 | *cp = CNULL; |
---|
786 | |
---|
787 | if (!volmgt_acquire(VolPath, ProgramName, 0, &ErrPtr, &ProcID)) { |
---|
788 | SImsg(SIM_GERR, "%s: volmgt_acquire failed: %s", VolPath, SYSERR); |
---|
789 | return((char *) NULL); |
---|
790 | } |
---|
791 | |
---|
792 | return(VolPath); |
---|
793 | } |
---|
794 | |
---|
795 | /* |
---|
796 | * Release device VolPath by calling the volmgt_* API. |
---|
797 | */ |
---|
798 | static char *VolMgtRelease(VolPath) |
---|
799 | char *VolPath; |
---|
800 | { |
---|
801 | if (!volmgt_release(VolPath)) |
---|
802 | SImsg(SIM_GERR, "%s: volmgt_release failed: %s", VolPath, SYSERR); |
---|
803 | |
---|
804 | (void) free(VolPath); |
---|
805 | } |
---|
806 | |
---|
807 | /* |
---|
808 | * Use VolMgt API to get attributes for a device. |
---|
809 | * This only works if media is loaded. |
---|
810 | */ |
---|
811 | static void VolMgtGetAttr(ProbeData) |
---|
812 | ProbeData_t *ProbeData; |
---|
813 | { |
---|
814 | DevInfo_t *DevInfo = NULL; |
---|
815 | char *VolPath; |
---|
816 | char *cp; |
---|
817 | |
---|
818 | if (!ProbeData) |
---|
819 | return; |
---|
820 | |
---|
821 | DevInfo = ProbeData->UseDevInfo; |
---|
822 | VolPath = ProbeData->DevFile; |
---|
823 | |
---|
824 | if (cp = media_getattr(VolPath, "s-access")) { |
---|
825 | if (EQ(cp, "seq")) |
---|
826 | cp = "Sequential Access"; |
---|
827 | else if (EQ(cp, "rand")) |
---|
828 | cp = "Random Access"; |
---|
829 | AddDevDesc(DevInfo, cp, "Access Type", DA_APPEND); |
---|
830 | } |
---|
831 | |
---|
832 | if (cp = media_getattr(VolPath, "s-density")) |
---|
833 | AddDevDesc(DevInfo, cp, "Media Density", DA_APPEND); |
---|
834 | |
---|
835 | if (cp = media_getattr(VolPath, "s-parts")) |
---|
836 | AddDevDesc(DevInfo, cp, "Partitions", DA_APPEND); |
---|
837 | |
---|
838 | if (cp = media_getattr(VolPath, "s-location")) |
---|
839 | AddDevDesc(DevInfo, cp, "Location", DA_APPEND); |
---|
840 | |
---|
841 | if (cp = media_getattr(VolPath, "s-mejectable")) |
---|
842 | AddDevDesc(DevInfo, cp, "Has Manual Eject", DA_APPEND); |
---|
843 | } |
---|
844 | #endif /* HAVE_VOLMGT */ |
---|
845 | |
---|
846 | /* |
---|
847 | * Probe a specific disk drive as file DevFile. |
---|
848 | * The UsingVolMgt flag indicates whether we're using VolMgt on this call. |
---|
849 | */ |
---|
850 | static DevInfo_t *ProbeDiskDriveFile(ProbeData, UsingVolMgt) |
---|
851 | ProbeData_t *ProbeData; |
---|
852 | int UsingVolMgt; |
---|
853 | { |
---|
854 | DevInfo_t *DevInfo = NULL; |
---|
855 | DKcinfo *dk_cinfo; |
---|
856 | DKgeom *dk_geom; |
---|
857 | DKtype *dk_type; |
---|
858 | DKvtoc *dk_vtoc; |
---|
859 | char *DevName; |
---|
860 | char *cp; |
---|
861 | int CDspeed = 0; |
---|
862 | int Len; |
---|
863 | int fd; |
---|
864 | int Status; |
---|
865 | char *DiskName; |
---|
866 | char *VolDevFile = NULL; |
---|
867 | char *DevFile; |
---|
868 | DevData_t *DevData; |
---|
869 | DevDefine_t *DevDefine; |
---|
870 | |
---|
871 | if (!ProbeData || !(DevFile = ProbeData->DevFile)) { |
---|
872 | SImsg(SIM_GERR, "ProbeDiskDriveFile: Missing parameters."); |
---|
873 | return((DevInfo_t *)NULL); |
---|
874 | } |
---|
875 | DiskName = ProbeData->DevName; |
---|
876 | DevData = ProbeData->DevData; |
---|
877 | DevDefine = ProbeData->DevDefine; |
---|
878 | |
---|
879 | /* Use basename of DevInfo file for virtual disk device name */ |
---|
880 | DevName = strdup(DevFile); |
---|
881 | if (cp = strrchr(DevName, '/')) |
---|
882 | DevName = ++cp; |
---|
883 | if (EQN(DevFile, "/dev/rdsk/c", 11)) { |
---|
884 | /* Zap slice part of name */ |
---|
885 | if (cp = strrchr(DevName, 's')) |
---|
886 | *cp = CNULL; |
---|
887 | } |
---|
888 | |
---|
889 | /* |
---|
890 | * Set ProbeData for other function calls |
---|
891 | */ |
---|
892 | ProbeData->DevFile = DevFile; |
---|
893 | ProbeData->DevName = DevName; |
---|
894 | |
---|
895 | /* |
---|
896 | * Try opening the disk device. Usually this will be the "s0" device. |
---|
897 | * If that fails, try opening "s2". Sometimes there's no "s0" |
---|
898 | * partition, but there is an "s2". |
---|
899 | * |
---|
900 | * The O_NDELAY flag will allow the device to be opened even if no |
---|
901 | * media is loaded. |
---|
902 | */ |
---|
903 | fd = open(DevFile, O_RDONLY|O_NDELAY); |
---|
904 | ProbeData->FileDesc = fd; |
---|
905 | if (fd < 0) { |
---|
906 | Len = strlen(DevFile); |
---|
907 | if (DevFile[Len-1] == '0') { |
---|
908 | DevFile[Len-1] = '2'; |
---|
909 | fd = open(DevFile, O_RDONLY|O_NDELAY); |
---|
910 | } |
---|
911 | } |
---|
912 | if (fd < 0) { |
---|
913 | SImsg(SIM_GERR, "%s: Cannot open for reading: %s.", DevFile, SYSERR); |
---|
914 | #if defined(HAVE_VOLMGT) |
---|
915 | if (errno == EBUSY && !UsingVolMgt) { |
---|
916 | /* |
---|
917 | * The device is busy, so let's try acquiring the device from |
---|
918 | * the Volume Manager. If that succeeds, call this function |
---|
919 | * again, but use the Vol Manager device pathname. |
---|
920 | */ |
---|
921 | if (VolDevFile = VolMgtAcquire(ProbeData)) { |
---|
922 | ProbeData->DevFile = VolDevFile; |
---|
923 | DevInfo = ProbeDiskDriveFile(ProbeData, TRUE); |
---|
924 | ProbeData->UseDevInfo = DevInfo; |
---|
925 | VolMgtGetAttr(ProbeData); |
---|
926 | VolMgtRelease(VolDevFile); |
---|
927 | return(DevInfo); |
---|
928 | } |
---|
929 | } |
---|
930 | #endif /* HAVE_VOLMGT */ |
---|
931 | if (errno == EBUSY || |
---|
932 | ((DevDefine->Model || DevDefine->Desc || DevData->DevNum > 0) && |
---|
933 | FLAGS_ON(DevData->Flags, DD_IS_ALIVE))) { |
---|
934 | /* |
---|
935 | * If we know for sure this drive is present and we |
---|
936 | * know something about it, then create a minimal device. |
---|
937 | */ |
---|
938 | return(CreateBaseDiskDrive(ProbeData, DiskName)); |
---|
939 | } |
---|
940 | return((DevInfo_t *) NULL); |
---|
941 | } |
---|
942 | |
---|
943 | Status = -1; |
---|
944 | #if defined(CDROMGDRVSPEED) |
---|
945 | /* |
---|
946 | * If this is a DT_DISKDRIVE, it could really be a CDROM, so this is how |
---|
947 | * we tell the difference. |
---|
948 | */ |
---|
949 | if (DevDefine->Type == DT_DISKDRIVE || DevDefine->Type == DT_CDROM) { |
---|
950 | Status = ioctl(fd, CDROMGDRVSPEED, &CDspeed); |
---|
951 | if (Status != 0) |
---|
952 | SImsg(SIM_GERR, "%s: ioctl CDROMGDRVSPEED failed: %s", |
---|
953 | DevFile, SYSERR); |
---|
954 | } |
---|
955 | #endif |
---|
956 | if (Status == 0) |
---|
957 | /* |
---|
958 | * It's definetely a CDROM. |
---|
959 | */ |
---|
960 | DevInfo = CreateCDROM(ProbeData, DevName, CDspeed); |
---|
961 | else if (DevDefine->Type == DT_DISKDRIVE) { |
---|
962 | /* |
---|
963 | * It's a normal Disk Drive. |
---|
964 | */ |
---|
965 | if ((dk_vtoc = GETvtoc(fd, DevFile)) == NULL) |
---|
966 | SImsg(SIM_GERR, "%s: get vtoc failed.", DevFile); |
---|
967 | if ((dk_cinfo = GETdk_cinfo(fd, DevFile)) == NULL) |
---|
968 | SImsg(SIM_GERR, "%s: get dk_cinfo failed.", DevFile); |
---|
969 | if ((dk_geom = GETdk_geom(fd, DevFile)) == NULL) |
---|
970 | SImsg(SIM_GERR, "%s: get dk_geom failed.", DevFile); |
---|
971 | #if defined(HAVE_HDIO) |
---|
972 | if ((dk_type = GETdk_type(fd, DevFile)) == NULL) |
---|
973 | SImsg(SIM_DBG, "%s: no dk_type info available.", DevFile); |
---|
974 | #endif /* HAVE_HDIO */ |
---|
975 | |
---|
976 | DevInfo = CreateDiskDrive(ProbeData, DevName, |
---|
977 | dk_vtoc, dk_cinfo, dk_geom, dk_type); |
---|
978 | if (!DevInfo) |
---|
979 | SImsg(SIM_GERR, "%s: Cannot convert DiskDrive information.", |
---|
980 | DevName); |
---|
981 | } |
---|
982 | if (!DevInfo) |
---|
983 | /* |
---|
984 | * If we still haven't gotten any information on what type of |
---|
985 | * DiskDrive this really is, create a basic entry anyway. |
---|
986 | */ |
---|
987 | DevInfo = CreateBaseDiskDrive(ProbeData, DiskName); |
---|
988 | |
---|
989 | close(fd); |
---|
990 | ProbeData->FileDesc = -1; |
---|
991 | |
---|
992 | return(ProbeData->RetDevInfo = DevInfo); |
---|
993 | } |
---|
994 | |
---|
995 | /* |
---|
996 | * Probe a disk drive by general name (DiskName) |
---|
997 | */ |
---|
998 | extern DevInfo_t *ProbeDiskDrive(ProbeData) |
---|
999 | ProbeData_t *ProbeData; |
---|
1000 | { |
---|
1001 | char **FileList; |
---|
1002 | int FileCount; |
---|
1003 | register int i; |
---|
1004 | char *DiskName; |
---|
1005 | |
---|
1006 | if (!ProbeData || !(DiskName = ProbeData->DevName)) |
---|
1007 | return((DevInfo_t *)NULL); |
---|
1008 | |
---|
1009 | FileCount = GetDeviceFile(ProbeData, _PATH_DEV_RDSK, &FileList); |
---|
1010 | if (FileCount < 1) { |
---|
1011 | SImsg(SIM_GERR, "Cannot find disk device file for <%s>.", DiskName); |
---|
1012 | return((DevInfo_t *)NULL); |
---|
1013 | } |
---|
1014 | |
---|
1015 | for (i = 0; i < FileCount; ++i) { |
---|
1016 | ProbeData->DevFile = FileList[i]; |
---|
1017 | if (DevInfo = ProbeDiskDriveFile(ProbeData, FALSE)) |
---|
1018 | return(ProbeData->RetDevInfo = DevInfo); |
---|
1019 | } |
---|
1020 | |
---|
1021 | return((DevInfo_t *)NULL); |
---|
1022 | } |
---|
1023 | |
---|
1024 | /* |
---|
1025 | * Probe a Floppy device referenced by ProbeData->DevFile. |
---|
1026 | * UsingVolMgt means we're using VolMgt on this call. |
---|
1027 | */ |
---|
1028 | static DevInfo_t *ProbeFloppyFile(ProbeData, UsingVolMgt) |
---|
1029 | ProbeData_t *ProbeData; |
---|
1030 | int UsingVolMgt; |
---|
1031 | { |
---|
1032 | DevInfo_t *DevInfo = NULL; |
---|
1033 | static struct fd_char FDchar; |
---|
1034 | static struct fd_drive FDdrive; |
---|
1035 | char *DevName; |
---|
1036 | int fd; |
---|
1037 | char *VolDevFile = NULL; |
---|
1038 | char *DevFile; |
---|
1039 | DevData_t *DevData; |
---|
1040 | DevDefine_t *DevDefine; |
---|
1041 | DiskDriveData_t *DiskDriveData = NULL; |
---|
1042 | DiskDrive_t *Disk = NULL; |
---|
1043 | |
---|
1044 | if (!ProbeData || !ProbeData->DevFile) |
---|
1045 | return((DevInfo_t *)NULL); |
---|
1046 | |
---|
1047 | DevFile = ProbeData->DevFile; |
---|
1048 | DevData = ProbeData->DevData; |
---|
1049 | DevDefine = ProbeData->DevDefine; |
---|
1050 | DevData->DevType = DT_FLOPPY; |
---|
1051 | |
---|
1052 | /* |
---|
1053 | * Try opening the disk device. Usually this will be the "s0" device. |
---|
1054 | * If that fails, try opening "s2". Sometimes there's no "s0" |
---|
1055 | * partition, but there is an "s2". |
---|
1056 | * |
---|
1057 | * The O_NDELAY flag will allow the device to be opened even if no |
---|
1058 | * media is loaded. |
---|
1059 | */ |
---|
1060 | fd = open(DevFile, O_RDONLY|O_NDELAY|O_NONBLOCK); |
---|
1061 | ProbeData->FileDesc = fd; |
---|
1062 | if (fd < 0) { |
---|
1063 | SImsg(SIM_GERR, "%s: Cannot open for reading: %s.", DevFile, SYSERR); |
---|
1064 | #if defined(HAVE_VOLMGT) |
---|
1065 | if (errno == EBUSY && !UsingVolMgt) { |
---|
1066 | /* |
---|
1067 | * The device is busy, so let's try acquiring the device from |
---|
1068 | * the Volume Manager. If that succeeds, call this function |
---|
1069 | * again, but use the Vol Manager device pathname. |
---|
1070 | */ |
---|
1071 | if (VolDevFile = VolMgtAcquire(ProbeData)) { |
---|
1072 | ProbeData->DevFile = VolDevFile; |
---|
1073 | DevInfo = ProbeDiskDriveFile(ProbeData, TRUE); |
---|
1074 | ProbeData->UseDevInfo = DevInfo; |
---|
1075 | VolMgtGetAttr(ProbeData); |
---|
1076 | VolMgtRelease(VolDevFile); |
---|
1077 | return(DevInfo); |
---|
1078 | } |
---|
1079 | } |
---|
1080 | #endif /* HAVE_VOLMGT */ |
---|
1081 | if (errno == EBUSY || |
---|
1082 | ((DevDefine->Model || DevDefine->Desc || DevData->DevNum > 0) && |
---|
1083 | FLAGS_ON(DevData->Flags, DD_IS_ALIVE))) { |
---|
1084 | /* |
---|
1085 | * If we know for sure this drive is present and we |
---|
1086 | * know something about it, then create a minimal device. |
---|
1087 | */ |
---|
1088 | return(DeviceCreate(ProbeData)); |
---|
1089 | } |
---|
1090 | return((DevInfo_t *) NULL); |
---|
1091 | } |
---|
1092 | |
---|
1093 | if (!DevInfo) |
---|
1094 | DevInfo = DeviceCreate(ProbeData); |
---|
1095 | |
---|
1096 | (void) memset(&FDchar, 0, sizeof(FDchar)); |
---|
1097 | if (ioctl(fd, FDIOGCHAR, &FDchar) < 0) { |
---|
1098 | SImsg(SIM_DBG, "%s: ioctl FDIOGCHAR failed: %s", DevFile, SYSERR); |
---|
1099 | } else { |
---|
1100 | /* Setup Disk info */ |
---|
1101 | if (DevInfo->DevSpec) |
---|
1102 | DiskDriveData = (DiskDriveData_t *) DevInfo->DevSpec; |
---|
1103 | else { |
---|
1104 | DiskDriveData = NewDiskDriveData(NULL); |
---|
1105 | DevInfo->DevSpec = (void *) DiskDriveData; |
---|
1106 | } |
---|
1107 | if (DiskDriveData->HWdata) |
---|
1108 | Disk = DiskDriveData->HWdata; |
---|
1109 | else { |
---|
1110 | Disk = NewDiskDrive(NULL); |
---|
1111 | DiskDriveData->HWdata = Disk; |
---|
1112 | } |
---|
1113 | /* Set what we got */ |
---|
1114 | Disk->Tracks = FDchar.fdc_nhead; |
---|
1115 | Disk->SecSize = FDchar.fdc_sec_size; |
---|
1116 | Disk->Sect = FDchar.fdc_secptrack; |
---|
1117 | Disk->DataCyl = FDchar.fdc_ncyl; |
---|
1118 | Disk->StepsPerTrack = FDchar.fdc_steps; |
---|
1119 | if (FDchar.fdc_transfer_rate > 0) |
---|
1120 | AddDevDesc(DevInfo, itoa(FDchar.fdc_transfer_rate), |
---|
1121 | "Transfer Rate (Kb/s)", DA_APPEND); |
---|
1122 | } |
---|
1123 | |
---|
1124 | (void) memset(&FDdrive, 0, sizeof(FDdrive)); |
---|
1125 | if (ioctl(fd, FDGETDRIVECHAR, &FDdrive) < 0) { |
---|
1126 | SImsg(SIM_DBG, "%s: ioctl FDGETDRIVECHAR failed: %s", DevFile, SYSERR); |
---|
1127 | } else { |
---|
1128 | if (FDdrive.fdd_ejectable > 0) |
---|
1129 | AddDevDesc(DevInfo, "Manual Eject", "Has", DA_APPEND); |
---|
1130 | if (FDdrive.fdd_maxsearch > 0) |
---|
1131 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_maxsearch), |
---|
1132 | "Size of per unit search table", DA_APPEND); |
---|
1133 | if (FDdrive.fdd_writeprecomp > 0) |
---|
1134 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_writeprecomp), |
---|
1135 | "Precompensation start cylinder", DA_APPEND); |
---|
1136 | if (FDdrive.fdd_writereduce > 0) |
---|
1137 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_writereduce), |
---|
1138 | "Reduce write current start cylinder", DA_APPEND); |
---|
1139 | if (FDdrive.fdd_stepwidth > 0) |
---|
1140 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_stepwidth), |
---|
1141 | "Width of step pulse (1us)", DA_APPEND); |
---|
1142 | if (FDdrive.fdd_steprate > 0) |
---|
1143 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_steprate), |
---|
1144 | "Step Rate (100us)", DA_APPEND); |
---|
1145 | if (FDdrive.fdd_headsettle > 0) |
---|
1146 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_headsettle), |
---|
1147 | "Head settle delay (100us)", DA_APPEND); |
---|
1148 | if (FDdrive.fdd_headload > 0) |
---|
1149 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_headload), |
---|
1150 | "Head load delay (100us)", DA_APPEND); |
---|
1151 | if (FDdrive.fdd_headunload > 0) |
---|
1152 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_headunload), |
---|
1153 | "Head unload delay (100us)", DA_APPEND); |
---|
1154 | if (FDdrive.fdd_motoron > 0) |
---|
1155 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_motoron), |
---|
1156 | "Motor On delay (100us)", DA_APPEND); |
---|
1157 | if (FDdrive.fdd_motoroff > 0) |
---|
1158 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_motoroff), |
---|
1159 | "Motor Off delay (100us)", DA_APPEND); |
---|
1160 | if (FDdrive.fdd_precomplevel > 0) |
---|
1161 | AddDevDesc(DevInfo, itoa(FDdrive.fdd_precomplevel), |
---|
1162 | "Pre Comp Level (ns)", DA_APPEND); |
---|
1163 | } |
---|
1164 | |
---|
1165 | close(fd); |
---|
1166 | ProbeData->FileDesc = -1; |
---|
1167 | |
---|
1168 | return(ProbeData->RetDevInfo = DevInfo); |
---|
1169 | } |
---|
1170 | |
---|
1171 | /* |
---|
1172 | * Probe a floppy disk drive |
---|
1173 | */ |
---|
1174 | extern DevInfo_t *ProbeFloppy(ProbeData) |
---|
1175 | ProbeData_t *ProbeData; |
---|
1176 | { |
---|
1177 | char **FileList; |
---|
1178 | int FileCount; |
---|
1179 | register int i; |
---|
1180 | char *DevName; |
---|
1181 | |
---|
1182 | if (!ProbeData || !(DevName = ProbeData->DevName)) |
---|
1183 | return((DevInfo_t *)NULL); |
---|
1184 | |
---|
1185 | FileCount = GetDeviceFile(ProbeData, _PATH_DEV_RDSK, &FileList); |
---|
1186 | if (FileCount < 1) { |
---|
1187 | SImsg(SIM_GERR, "%s: Cannot find floppy device file.", DevName); |
---|
1188 | return((DevInfo_t *)NULL); |
---|
1189 | } |
---|
1190 | |
---|
1191 | for (i = 0; i < FileCount; ++i) { |
---|
1192 | ProbeData->DevFile = FileList[i]; |
---|
1193 | if (DevInfo = ProbeFloppyFile(ProbeData, FALSE)) |
---|
1194 | return(ProbeData->RetDevInfo = DevInfo); |
---|
1195 | } |
---|
1196 | |
---|
1197 | return((DevInfo_t *)NULL); |
---|
1198 | } |
---|
1199 | |
---|
1200 | /* |
---|
1201 | * Get hostid |
---|
1202 | */ |
---|
1203 | long gethostid() |
---|
1204 | { |
---|
1205 | char Buff[64]; |
---|
1206 | |
---|
1207 | sysinfo(SI_HW_SERIAL, Buff, sizeof(Buff)); |
---|
1208 | |
---|
1209 | return(atol(Buff)); |
---|
1210 | } |
---|
1211 | |
---|
1212 | /* |
---|
1213 | * Get system page size |
---|
1214 | */ |
---|
1215 | int getpagesize() |
---|
1216 | { |
---|
1217 | long siz; |
---|
1218 | |
---|
1219 | if ((siz = sysconf(_SC_PAGESIZE)) == -1) { |
---|
1220 | SImsg(SIM_GERR, "Cannot get pagesize from sysconf(): %s", SYSERR); |
---|
1221 | return(0); |
---|
1222 | } |
---|
1223 | |
---|
1224 | return((int) siz); |
---|
1225 | } |
---|
1226 | |
---|
1227 | #if defined(HAVE_SUNROMVEC) |
---|
1228 | #include <sys/comvec.h> |
---|
1229 | #endif /* HAVE_SUNROMVEC */ |
---|
1230 | |
---|
1231 | /* |
---|
1232 | * Get ROM Version number |
---|
1233 | */ |
---|
1234 | extern char *GetRomVerSun() |
---|
1235 | { |
---|
1236 | static char RomRev[32]; |
---|
1237 | #if defined(HAVE_SUNROMVEC) |
---|
1238 | struct nlist *nlptr; |
---|
1239 | static union sunromvec Rom; |
---|
1240 | kvm_t *kd; |
---|
1241 | union sunromvec *romp; |
---|
1242 | |
---|
1243 | if (!(kd = KVMopen())) |
---|
1244 | return((char *) NULL); |
---|
1245 | |
---|
1246 | if ((nlptr = KVMnlist(kd, RomVecSYM, (struct nlist *)NULL, 0)) == NULL) |
---|
1247 | return((char *) NULL); |
---|
1248 | |
---|
1249 | if (CheckNlist(nlptr)) |
---|
1250 | return((char *) NULL); |
---|
1251 | |
---|
1252 | /* |
---|
1253 | * Read the kernel pointer to the sunromvec structure. |
---|
1254 | */ |
---|
1255 | if (KVMget(kd, (u_long) nlptr->n_value, (char *) &romp, |
---|
1256 | sizeof(romp), KDT_DATA)) { |
---|
1257 | SImsg(SIM_GERR, "Cannot read sunromvec pointer from kernel."); |
---|
1258 | return((char *) NULL); |
---|
1259 | } |
---|
1260 | |
---|
1261 | /* |
---|
1262 | * Read sunromvec from the kernel |
---|
1263 | */ |
---|
1264 | if (KVMget(kd, (u_long) romp, (char *) &Rom, |
---|
1265 | sizeof(union sunromvec), KDT_DATA)) { |
---|
1266 | SImsg(SIM_GERR, "Cannot read sunromvec from kernel."); |
---|
1267 | return((char *) NULL); |
---|
1268 | } |
---|
1269 | |
---|
1270 | if (Rom.sunmon.v_mon_id) { |
---|
1271 | /* |
---|
1272 | * Read the version string from the address indicated by v_mon_id. |
---|
1273 | * Read 1 byte at a time until '\0' is encountered. |
---|
1274 | */ |
---|
1275 | if (KVMget(kd, (u_long) Rom.sunmon.v_mon_id, RomRev, |
---|
1276 | sizeof(RomRev), KDT_STRING)) { |
---|
1277 | SImsg(SIM_GERR, "Cannot read sunmon rom revision from kernel."); |
---|
1278 | return((char *) NULL); |
---|
1279 | } |
---|
1280 | } |
---|
1281 | |
---|
1282 | if (Rom.obp.op_mon_id && !RomRev[0]) { |
---|
1283 | /* |
---|
1284 | * XXX Hardcoded values |
---|
1285 | */ |
---|
1286 | (void) snprintf(RomRev, sizeof(RomRev), "%d.%d", |
---|
1287 | Rom.obp.op_mon_id >> 16, Rom.obp.op_mon_id & 0xFFFF); |
---|
1288 | } |
---|
1289 | |
---|
1290 | KVMclose(kd); |
---|
1291 | |
---|
1292 | #endif /* HAVE_SUNROMVEC */ |
---|
1293 | |
---|
1294 | return((RomRev[0]) ? RomRev : (char *) NULL); |
---|
1295 | } |
---|
1296 | |
---|
1297 | /* |
---|
1298 | * Get amount of physical memory using sysmem() call. |
---|
1299 | */ |
---|
1300 | extern char *GetMemorySunOSsysmem() |
---|
1301 | { |
---|
1302 | char *Val = NULL; |
---|
1303 | #if defined(USE_SYSMEM) |
---|
1304 | /* |
---|
1305 | * sysmem() is an undocumented function that returns the |
---|
1306 | * amount of system (physical) memory in bytes. It's in |
---|
1307 | * /usr/lib/libadm.a. Discovered by reference in Sun |
---|
1308 | * PatchID 101050-01. |
---|
1309 | * |
---|
1310 | * NOTE: sysmem() is broken in SunOS 5.5 and 5.5.1 so we avoid it. |
---|
1311 | */ |
---|
1312 | long Amount; |
---|
1313 | |
---|
1314 | Amount = sysmem(); |
---|
1315 | if (Amount < 0) { |
---|
1316 | SImsg(SIM_GERR, "sysmem() failed: %s", SYSERR); |
---|
1317 | return((char *) NULL); |
---|
1318 | } |
---|
1319 | |
---|
1320 | Val = GetMemoryStr(DivRndUp(Amount, (Large_t) MBYTES)); |
---|
1321 | #endif /* USE_SYSMEM */ |
---|
1322 | return(Val); |
---|
1323 | } |
---|
1324 | |
---|
1325 | /* |
---|
1326 | * Get amount of physical memory using sysconf() |
---|
1327 | */ |
---|
1328 | extern char *GetMemorySunOSsysconf() |
---|
1329 | { |
---|
1330 | char *Val = NULL; |
---|
1331 | #if defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) |
---|
1332 | long long Amount; |
---|
1333 | long PageSize; |
---|
1334 | long NumPages; |
---|
1335 | |
---|
1336 | PageSize = sysconf(_SC_PAGESIZE); |
---|
1337 | NumPages = sysconf(_SC_PHYS_PAGES); |
---|
1338 | if (PageSize < 0 || NumPages < 0) { |
---|
1339 | SImsg(SIM_GERR, "sysconf() failed: %s", SYSERR); |
---|
1340 | return((char *) NULL); |
---|
1341 | } |
---|
1342 | Amount = (long long) PageSize * NumPages; |
---|
1343 | |
---|
1344 | Val = GetMemoryStr((int) ((Amount+MBYTES-1) / MBYTES), MBYTES); |
---|
1345 | #endif /* _SC_* */ |
---|
1346 | return(Val); |
---|
1347 | } |
---|
1348 | |
---|
1349 | /* |
---|
1350 | * Get the system to detect all disk drives on the system. |
---|
1351 | * We only open the "s2" file for each device to avoid a |
---|
1352 | * performance hit of opening every device file. |
---|
1353 | */ |
---|
1354 | static void DetectDisks() |
---|
1355 | { |
---|
1356 | static DIR *DirPtr; |
---|
1357 | struct dirent *DirEnt; |
---|
1358 | static char DevName[MAXPATHLEN]; |
---|
1359 | static char PathName[MAXPATHLEN]; |
---|
1360 | static namelist_t *UsedList = NULL; |
---|
1361 | register char *cp; |
---|
1362 | int FileD; |
---|
1363 | |
---|
1364 | if (!(DirPtr = opendir(_PATH_DEV_RDSK))) { |
---|
1365 | SImsg(SIM_GERR, "Cannot open directory %s: %s.", |
---|
1366 | _PATH_DEV_RDSK, SYSERR); |
---|
1367 | return; |
---|
1368 | } |
---|
1369 | |
---|
1370 | while (DirEnt = readdir(DirPtr)) { |
---|
1371 | if (EQ(DirEnt->d_name, ".") || EQ(DirEnt->d_name, "..")) |
---|
1372 | continue; |
---|
1373 | |
---|
1374 | (void) strcpy(DevName, DirEnt->d_name); |
---|
1375 | if (cp = strchr(DevName, 's')) |
---|
1376 | *cp = CNULL; |
---|
1377 | if (NameListFind(UsedList, DevName)) |
---|
1378 | continue; |
---|
1379 | |
---|
1380 | /* |
---|
1381 | * Always use slice 2 which should always |
---|
1382 | * have a partition table. |
---|
1383 | */ |
---|
1384 | (void) snprintf(PathName, sizeof(PathName), "%s/%ss2", _PATH_DEV_RDSK, DevName); |
---|
1385 | if ((FileD = open(PathName, O_RDONLY)) > 0) |
---|
1386 | (void) close(FileD); |
---|
1387 | NameListAdd(&UsedList, DevName); |
---|
1388 | } |
---|
1389 | |
---|
1390 | NameListFree(UsedList); |
---|
1391 | (void) closedir(DirPtr); |
---|
1392 | } |
---|
1393 | |
---|
1394 | /* |
---|
1395 | * Get the system to detect all tape drives on the system. |
---|
1396 | */ |
---|
1397 | static void DetectTapes() |
---|
1398 | { |
---|
1399 | static DIR *DirPtr; |
---|
1400 | struct dirent *DirEnt; |
---|
1401 | static char DevName[MAXPATHLEN]; |
---|
1402 | static char PathName[MAXPATHLEN]; |
---|
1403 | static namelist_t *UsedList = NULL; |
---|
1404 | register char *cp; |
---|
1405 | int FileD; |
---|
1406 | |
---|
1407 | if (!(DirPtr = opendir(_PATH_DEV_RMT))) { |
---|
1408 | SImsg(SIM_GERR, "Cannot open directory %s: %s.", |
---|
1409 | _PATH_DEV_RMT, SYSERR); |
---|
1410 | return; |
---|
1411 | } |
---|
1412 | |
---|
1413 | while (DirEnt = readdir(DirPtr)) { |
---|
1414 | if (EQ(DirEnt->d_name, ".") || EQ(DirEnt->d_name, "..")) |
---|
1415 | continue; |
---|
1416 | |
---|
1417 | (void) strcpy(DevName, DirEnt->d_name); |
---|
1418 | for (cp = DevName; cp && isdigit(*cp); ++cp); |
---|
1419 | if (cp) |
---|
1420 | *cp = CNULL; |
---|
1421 | if (NameListFind(UsedList, DevName)) |
---|
1422 | continue; |
---|
1423 | |
---|
1424 | /* |
---|
1425 | * Always use the "n" device to avoid rewinding the tape. |
---|
1426 | */ |
---|
1427 | (void) snprintf(PathName, sizeof(PathName), "%s/%sn", _PATH_DEV_RMT, DevName); |
---|
1428 | if ((FileD = open(PathName, O_RDONLY)) > 0) |
---|
1429 | (void) close(FileD); |
---|
1430 | NameListAdd(&UsedList, DevName); |
---|
1431 | } |
---|
1432 | |
---|
1433 | NameListFree(UsedList); |
---|
1434 | (void) closedir(DirPtr); |
---|
1435 | } |
---|
1436 | |
---|
1437 | /* |
---|
1438 | * Get the system to detect the floppy disk drive. |
---|
1439 | */ |
---|
1440 | static int DetectFloppy() |
---|
1441 | { |
---|
1442 | static char DevName[MAXPATHLEN]; |
---|
1443 | int FileD; |
---|
1444 | |
---|
1445 | (void) snprintf(DevName, sizeof(DevName), "%s/rdiskette", _PATH_DEV); |
---|
1446 | if ((FileD = open(DevName, O_RDONLY)) > 0) |
---|
1447 | (void) close(FileD); |
---|
1448 | } |
---|
1449 | |
---|
1450 | /* |
---|
1451 | * The system does not "see" all devices until something attempts |
---|
1452 | * to first use them. These routines will cause the system to look |
---|
1453 | * for and "see" certain devices that are not normally seen after |
---|
1454 | * a system boot. |
---|
1455 | */ |
---|
1456 | extern void DetectDevices() |
---|
1457 | { |
---|
1458 | DetectDisks(); |
---|
1459 | DetectTapes(); |
---|
1460 | DetectFloppy(); |
---|
1461 | } |
---|
1462 | |
---|
1463 | /* |
---|
1464 | * There's no easy way to get this out of /kernel/unix so we |
---|
1465 | * do the same thing the kernel does. |
---|
1466 | */ |
---|
1467 | extern char *GetKernVerSunOS5() |
---|
1468 | { |
---|
1469 | static char Buff[128]; |
---|
1470 | static struct utsname un; |
---|
1471 | |
---|
1472 | if (uname(&un) == -1) |
---|
1473 | return((char *) NULL); |
---|
1474 | |
---|
1475 | (void) snprintf(Buff, sizeof(Buff), |
---|
1476 | "SunOS Release %s Version %s%s [UNIX(R) System V Release 4.0]", |
---|
1477 | un.release, un.version, |
---|
1478 | #ifdef _LP64 |
---|
1479 | " 64-bit" |
---|
1480 | #else |
---|
1481 | "" |
---|
1482 | #endif |
---|
1483 | ); |
---|
1484 | |
---|
1485 | return(Buff); |
---|
1486 | } |
---|
1487 | |
---|
1488 | /* |
---|
1489 | * Get the OS Dist information by reading the first line of /etc/release |
---|
1490 | */ |
---|
1491 | extern char *GetOSDistSunOS5() |
---|
1492 | { |
---|
1493 | #if defined(OS_RELEASE_FILE) |
---|
1494 | static char Buff[256]; |
---|
1495 | FILE *fp; |
---|
1496 | register char *cp; |
---|
1497 | |
---|
1498 | fp = fopen(OS_RELEASE_FILE, "r"); |
---|
1499 | if (!fp) { |
---|
1500 | SImsg(SIM_GERR, "%s: Cannot open OS release file: %s", |
---|
1501 | OS_RELEASE_FILE, SYSERR); |
---|
1502 | return((char *) NULL); |
---|
1503 | } |
---|
1504 | |
---|
1505 | if (!fgets(Buff, sizeof(Buff), fp)) { |
---|
1506 | SImsg(SIM_GERR, "%s: Read OS release file failed: %s", |
---|
1507 | OS_RELEASE_FILE, SYSERR); |
---|
1508 | return((char *) NULL); |
---|
1509 | } |
---|
1510 | (void) fclose(fp); |
---|
1511 | |
---|
1512 | /* Zap trailing newline */ |
---|
1513 | if (cp = strchr(Buff, '\n')) |
---|
1514 | *cp = CNULL; |
---|
1515 | |
---|
1516 | /* Skip leading white space */ |
---|
1517 | for (cp = Buff; isspace(*cp); ++cp); |
---|
1518 | |
---|
1519 | return(cp); |
---|
1520 | #endif /* OS_RELEASE_FILE */ |
---|
1521 | } |
---|