1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <string.h>
27 #include <strings.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <device_info.h>
32 #include <bsm/devices.h>
33 #include <bsm/devalloc.h>
34 
35 char *strtok_r(char *, const char *, char **);
36 
37 /* externs from getdaent.c */
38 extern char *trim_white(char *);
39 extern int pack_white(char *);
40 extern char *getdadmfield(char *, char *);
41 extern int getdadmline(char *, int, FILE *);
42 
43 static struct _dmapbuff {
44 	FILE		*_dmapf;	/* for /etc/security/device_maps */
45 	devmap_t	_interpdevmap;
46 	char		_interpdmline[DA_BUFSIZE + 1];
47 	char		*_DEVMAP;
48 } *__dmapbuff;
49 
50 #define	dmapf	(_dmap->_dmapf)
51 #define	interpdevmap	(_dmap->_interpdevmap)
52 #define	interpdmline	(_dmap->_interpdmline)
53 #define	DEVMAPS_FILE	(_dmap->_DEVMAP)
54 
55 devmap_t	*dmap_interpret(char *, devmap_t *);
56 static devmap_t	*dmap_interpretf(char *, devmap_t *);
57 static devmap_t *dmap_dlexpand(devmap_t *);
58 
59 int	dmap_matchdev(devmap_t *, char *);
60 int	dmap_matchname(devmap_t *, char *);
61 
62 
63 /*
64  * _dmapalloc -
65  *	allocates common buffers and structures.
66  *	returns pointer to the new structure, else returns NULL on error.
67  */
68 static struct _dmapbuff *
69 _dmapalloc(void)
70 {
71 	struct _dmapbuff *_dmap = __dmapbuff;
72 
73 	if (_dmap == NULL) {
74 		_dmap = (struct _dmapbuff *)calloc((unsigned)1,
75 		    (unsigned)sizeof (*__dmapbuff));
76 		if (_dmap == NULL)
77 			return (NULL);
78 		DEVMAPS_FILE = "/etc/security/device_maps";
79 		dmapf = NULL;
80 		__dmapbuff = _dmap;
81 	}
82 
83 	return (_dmap);
84 }
85 
86 /*
87  * setdmapent -
88  *	rewinds the device_maps file to the beginning.
89  */
90 void
91 setdmapent(void)
92 {
93 	struct _dmapbuff *_dmap = _dmapalloc();
94 
95 	if (_dmap == NULL)
96 		return;
97 	if (dmapf == NULL)
98 		dmapf = fopen(DEVMAPS_FILE, "rF");
99 	else
100 		rewind(dmapf);
101 }
102 
103 /*
104  * enddmapent -
105  *	closes device_maps file.
106  */
107 void
108 enddmapent(void)
109 {
110 	struct _dmapbuff *_dmap = _dmapalloc();
111 
112 	if (_dmap == NULL)
113 		return;
114 	if (dmapf != NULL) {
115 		(void) fclose(dmapf);
116 		dmapf = NULL;
117 	}
118 }
119 
120 void
121 freedmapent(devmap_t *dmap)
122 {
123 	char	**darp;
124 
125 	if ((darp = dmap->dmap_devarray) != NULL) {
126 		while (*darp != NULL)
127 			free(*darp++);
128 		free(dmap->dmap_devarray);
129 		dmap->dmap_devarray = NULL;
130 	}
131 }
132 
133 /*
134  * setdmapfile -
135  *	changes the default device_maps file to the one specified.
136  *	It does not close the previous file. If this is desired, enddmapent
137  *	should be called prior to setdampfile.
138  */
139 void
140 setdmapfile(char *file)
141 {
142 	struct _dmapbuff *_dmap = _dmapalloc();
143 
144 	if (_dmap == NULL)
145 		return;
146 	if (dmapf != NULL) {
147 		(void) fclose(dmapf);
148 		dmapf = NULL;
149 	}
150 	DEVMAPS_FILE = file;
151 }
152 
153 /*
154  * getdmapent -
155  * 	When first called, returns a pointer to the first devmap_t structure
156  * 	in device_maps; thereafter, it returns a pointer to the next devmap_t
157  *	structure in the file. Thus successive calls can be used to read the
158  *	entire file.
159  *	call to getdmapent should be bracketed by setdmapent and enddmapent.
160  * 	returns pointer to devmap_t found, else returns NULL if no entry found
161  * 	or on error.
162  */
163 devmap_t *
164 getdmapent(void)
165 {
166 	devmap_t		*dmap;
167 	struct _dmapbuff 	*_dmap = _dmapalloc();
168 
169 	if ((_dmap == 0) || (dmapf == NULL))
170 		return (NULL);
171 
172 	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
173 	    dmapf) != 0) {
174 		if ((dmap = dmap_interpret(interpdmline,
175 		    &interpdevmap)) == NULL)
176 			continue;
177 		return (dmap);
178 	}
179 
180 	return (NULL);
181 }
182 
183 /*
184  * getdmapnam -
185  *	searches from the beginning of device_maps for the device specified by
186  *	its name.
187  *	call to getdmapnam should be bracketed by setdmapent and enddmapent.
188  * 	returns pointer to devmapt_t for the device if it is found, else
189  * 	returns NULL if device not found or in case of error.
190  */
191 devmap_t *
192 getdmapnam(char *name)
193 {
194 	devmap_t		*dmap;
195 	struct _dmapbuff	*_dmap = _dmapalloc();
196 
197 	if ((name == NULL) || (_dmap == 0) || (dmapf == NULL))
198 		return (NULL);
199 
200 	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
201 	    dmapf) != 0) {
202 		if (strstr(interpdmline, name) == NULL)
203 			continue;
204 		if ((dmap = dmap_interpretf(interpdmline,
205 		    &interpdevmap)) == NULL)
206 			continue;
207 		if (dmap_matchname(dmap, name)) {
208 			if ((dmap = dmap_dlexpand(dmap)) == NULL)
209 				continue;
210 			enddmapent();
211 			return (dmap);
212 		}
213 		freedmapent(dmap);
214 	}
215 
216 	return (NULL);
217 }
218 
219 /*
220  * getdmapdev -
221  *	searches from the beginning of device_maps for the device specified by
222  *	its logical name.
223  *	call to getdmapdev should be bracketed by setdmapent and enddmapent.
224  * 	returns  pointer to the devmap_t for the device if device is found,
225  *	else returns NULL if device not found or on error.
226  */
227 devmap_t *
228 getdmapdev(char *dev)
229 {
230 	devmap_t		*dmap;
231 	struct _dmapbuff	*_dmap = _dmapalloc();
232 
233 	if ((dev == NULL) || (_dmap == 0) || (dmapf == NULL))
234 		return (NULL);
235 
236 	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
237 	    dmapf) != 0) {
238 		if ((dmap = dmap_interpret(interpdmline,
239 		    &interpdevmap)) == NULL)
240 			continue;
241 		if (dmap_matchdev(dmap, dev)) {
242 			enddmapent();
243 			return (dmap);
244 		}
245 		freedmapent(dmap);
246 	}
247 
248 	return (NULL);
249 }
250 
251 /*
252  * getdmaptype -
253  *	searches from the beginning of device_maps for the device specified by
254  *	its type.
255  *	call to getdmaptype should be bracketed by setdmapent and enddmapent.
256  * 	returns pointer to devmap_t found, else returns NULL if no entry found
257  * 	or on error.
258  */
259 devmap_t *
260 getdmaptype(char *type)
261 {
262 	devmap_t		*dmap;
263 	struct _dmapbuff	*_dmap = _dmapalloc();
264 
265 	if ((type == NULL) || (_dmap == 0) || (dmapf == NULL))
266 		return (NULL);
267 
268 	while (getdadmline(interpdmline, (int)sizeof (interpdmline),
269 	    dmapf) != 0) {
270 		if ((dmap = dmap_interpretf(interpdmline,
271 		    &interpdevmap)) == NULL)
272 			continue;
273 		if (dmap->dmap_devtype != NULL &&
274 		    strcmp(type, dmap->dmap_devtype) == 0) {
275 			if ((dmap = dmap_dlexpand(dmap)) == NULL)
276 				continue;
277 			return (dmap);
278 		}
279 		freedmapent(dmap);
280 	}
281 
282 	return (NULL);
283 }
284 
285 /*
286  * dmap_match_one_dev -
287  *    Checks if the specified devmap_t contains strings
288  *    for the same logical link as the device specified.
289  *    This guarantees that the beginnings of a devlist build
290  *    match a more-complete devlist for the same device.
291  *
292  *    Returns 1 for a match, else returns 0.
293  */
294 static int
295 dmap_match_one_dev(devmap_t *dmap, char *dev)
296 {
297 	char **dva;
298 	char *dv;
299 
300 	if (dmap->dmap_devarray == NULL)
301 		return (0);
302 
303 	for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva++) {
304 		if (strstr(dev, dv) != NULL)
305 			return (1);
306 	}
307 	return (0);
308 }
309 
310 /*
311  * dmap_matchdev -
312  * 	checks if the specified devmap_t is for the device specified.
313  *	returns 1 if it is, else returns 0.
314  */
315 int
316 dmap_matchdev(devmap_t *dmap, char *dev)
317 {
318 	char **dva;
319 	char *dv;
320 
321 	if (dmap->dmap_devarray == NULL)
322 		return (0);
323 	for (dva = dmap->dmap_devarray; (dv = *dva) != NULL; dva ++) {
324 		if (strcmp(dv, dev) == 0)
325 			return (1);
326 	}
327 
328 	return (0);
329 }
330 
331 /*
332  * Requires a match of the /dev/?dsk links, not just the logical devname
333  * Returns 1 for match found, 0 for match not found, 2 for invalid arguments.
334  */
335 int
336 dmap_exact_dev(devmap_t *dmap, char *dev, int *num)
337 {
338 	char *dv;
339 
340 	if ((dev == NULL) || (dmap->dmap_devname == NULL))
341 		return (2);
342 	dv = dmap->dmap_devname;
343 	dv +=  strcspn(dmap->dmap_devname, "0123456789");
344 	if (sscanf(dv, "%d", num) != 1)
345 		return (2);
346 	/* during some add processes, dev can be shorter than dmap */
347 	return (dmap_match_one_dev(dmap, dev));
348 }
349 
350 /*
351  * dmap_matchtype -
352  *	checks if the specified devmap_t is for the device specified.
353  *	returns 1 if it is, else returns 0.
354  */
355 int
356 dmap_matchtype(devmap_t *dmap, char *type)
357 {
358 	if ((dmap->dmap_devtype == NULL) || (type == NULL))
359 		return (0);
360 
361 	return ((strcmp(dmap->dmap_devtype, type) == 0));
362 }
363 
364 /*
365  * dmap_matchname -
366  * 	checks if the specified devmap_t is for the device specified.
367  * 	returns 1 if it is, else returns 0.
368  */
369 int
370 dmap_matchname(devmap_t *dmap, char *name)
371 {
372 	if (dmap->dmap_devname == NULL)
373 		return (0);
374 
375 	return ((strcmp(dmap->dmap_devname, name) == 0));
376 }
377 
378 /*
379  * dmap_physname: path to /devices device
380  * Returns:
381  *	strdup'd (i.e. malloc'd) real device file if successful
382  *      NULL on error
383  */
384 char *
385 dmap_physname(devmap_t *dmap)
386 {
387 	char *oldlink;
388 	char stage_link[PATH_MAX + 1];
389 
390 	if ((dmap == NULL) || (dmap->dmap_devarray == NULL) ||
391 	    (dmap->dmap_devarray[0] == NULL))
392 		return (NULL);
393 
394 	(void) strncpy(stage_link, dmap->dmap_devarray[0], sizeof (stage_link));
395 
396 	if (devfs_resolve_link(stage_link, &oldlink) == 0)
397 		return (oldlink);
398 	return (NULL);
399 }
400 
401 /*
402  * dm_match -
403  *	calls dmap_matchname or dmap_matchtype as appropriate.
404  */
405 int
406 dm_match(devmap_t *dmap, da_args *dargs)
407 {
408 	if (dargs->devinfo->devname)
409 		return (dmap_matchname(dmap, dargs->devinfo->devname));
410 	else if (dargs->devinfo->devtype)
411 		return (dmap_matchtype(dmap, dargs->devinfo->devtype));
412 
413 	return (0);
414 }
415 
416 /*
417  * dmap_interpret -
418  *	calls dmap_interpretf and dmap_dlexpand to parse devmap_t line.
419  *	returns  pointer to parsed devmapt_t entry, else returns NULL on error.
420  */
421 devmap_t  *
422 dmap_interpret(char *val, devmap_t *dm)
423 {
424 	if (dmap_interpretf(val, dm) == NULL)
425 		return (NULL);
426 
427 	return (dmap_dlexpand(dm));
428 }
429 
430 /*
431  * dmap_interpretf -
432  * 	parses string "val" and initializes pointers in the given devmap_t to
433  * 	fields in "val".
434  * 	returns pointer to updated devmap_t.
435  */
436 static devmap_t  *
437 dmap_interpretf(char *val, devmap_t *dm)
438 {
439 	dm->dmap_devname = getdadmfield(val, KV_TOKEN_DELIMIT);
440 	dm->dmap_devtype = getdadmfield(NULL, KV_TOKEN_DELIMIT);
441 	dm->dmap_devlist = getdadmfield(NULL, KV_TOKEN_DELIMIT);
442 	dm->dmap_devarray = NULL;
443 	if (dm->dmap_devname == NULL ||
444 	    dm->dmap_devtype == NULL ||
445 	    dm->dmap_devlist == NULL)
446 		return (NULL);
447 
448 	return (dm);
449 }
450 
451 /*
452  * dmap_dlexpand -
453  * 	expands dmap_devlist of the form `devlist_generate`
454  *	returns unexpanded form if there is no '\`' or in case of error.
455  */
456 static devmap_t *
457 dmap_dlexpand(devmap_t *dmp)
458 {
459 	char	tmplist[DA_BUFSIZE + 1];
460 	char	*cp, *cpl, **darp;
461 	int	count;
462 	FILE	*expansion;
463 
464 	dmp->dmap_devarray = NULL;
465 	if (dmp->dmap_devlist == NULL)
466 		return (NULL);
467 	if (*(dmp->dmap_devlist) != '`') {
468 		(void) strcpy(tmplist, dmp->dmap_devlist);
469 	} else {
470 		(void) strcpy(tmplist, dmp->dmap_devlist + 1);
471 		if ((cp = strchr(tmplist, '`')) != NULL)
472 			*cp = '\0';
473 		if ((expansion = popen(tmplist, "rF")) == NULL)
474 			return (NULL);
475 		count = fread(tmplist, 1, sizeof (tmplist) - 1, expansion);
476 		(void) pclose(expansion);
477 		tmplist[count] = '\0';
478 	}
479 
480 	/* cleanup the list */
481 	count = pack_white(tmplist);
482 	dmp->dmap_devarray = darp =
483 	    (char **)malloc((count + 2) * sizeof (char *));
484 	if (darp == NULL)
485 		return (NULL);
486 	cp = tmplist;
487 	while ((cp = strtok_r(cp, " ", &cpl)) != NULL) {
488 		*darp = strdup(cp);
489 		if (*darp == NULL) {
490 			freedmapent(dmp);
491 			return (NULL);
492 		}
493 		darp++;
494 		cp = NULL;
495 	}
496 	*darp = NULL;
497 
498 	return (dmp);
499 }
500 
501 /*
502  * dmapskip -
503  * 	scans input string to find next colon or end of line.
504  *	returns pointer to next char.
505  */
506 static char *
507 dmapskip(char *p)
508 {
509 	while (*p && *p != ':' && *p != '\n')
510 		++p;
511 	if (*p == '\n')
512 		*p = '\0';
513 	else if (*p != '\0')
514 		*p++ = '\0';
515 
516 	return (p);
517 }
518 
519 /*
520  * dmapdskip -
521  * 	scans input string to find next space or end of line.
522  *	returns pointer to next char.
523  */
524 static char *
525 dmapdskip(p)
526 	register char *p;
527 {
528 	while (*p && *p != ' ' && *p != '\n')
529 		++p;
530 	if (*p != '\0')
531 		*p++ = '\0';
532 
533 	return (p);
534 }
535 
536 char *
537 getdmapfield(char *ptr)
538 {
539 	static	char	*tptr;
540 
541 	if (ptr == NULL)
542 		ptr = tptr;
543 	if (ptr == NULL)
544 		return (NULL);
545 	tptr = dmapskip(ptr);
546 	ptr = trim_white(ptr);
547 	if (ptr == NULL)
548 		return (NULL);
549 	if (*ptr == NULL)
550 		return (NULL);
551 
552 	return (ptr);
553 }
554 
555 char *
556 getdmapdfield(char *ptr)
557 {
558 	static	char	*tptr;
559 	if (ptr != NULL) {
560 		ptr = trim_white(ptr);
561 	} else {
562 		ptr = tptr;
563 	}
564 	if (ptr == NULL)
565 		return (NULL);
566 	tptr = dmapdskip(ptr);
567 	if (ptr == NULL)
568 		return (NULL);
569 	if (*ptr == NULL)
570 		return (NULL);
571 
572 	return (ptr);
573 }
574