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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1996-1997, by Sun Microsystems, Inc.
24 * All Rights reserved.
25 */
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29
30#pragma	ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.2 */
31/* LINTLIBRARY */
32
33/*
34 * putdev.c
35 *
36 * Global Definitions:
37 *	_adddevtabrec()		Add a record to the device table
38 *	_putdevtabrec()		Write a record to the device table
39 *	_moddevtabrec()		Modify a device-table record
40 *	_rmdevtabrec()		Remove a device-table record
41 *	_rmdevtabattrs()	Remove attributes from a device-table record
42 *	oam_devtab		File descriptor of the open device table
43 */
44
45/*
46 *  G L O B A L   R E F E R E N C E S
47 *
48 *	Header Files
49 *	Externals Referenced
50 */
51
52/*
53 * Header Files
54 *	<sys/types.h>		UNIX(r) Data Types
55 *	<sys/stat.h>
56 *	<stdio.h>		Standard I/O definitions
57 *	<fcntl.h>		Definitions for file control
58 *	<errno.h>		Error handling definitions
59 *	<string.h>		String Handling Definitions
60 *	<devmgmt.h>		Device Management Definitions
61 *	<unistd.h>		Get UNIX(r) Standard Definitions
62 *	"devtab.h"		Local Device Management Definitions
63 */
64
65#include	<sys/types.h>
66#include	<sys/stat.h>
67#include	<stdio.h>
68#include	<fcntl.h>
69#include	<errno.h>
70#include	<string.h>
71#include	<devmgmt.h>
72#include	<unistd.h>
73#include	<stdlib.h>
74#include	"devtab.h"
75
76/*
77 *  L O C A L   D E F I N I T I O N S
78 *
79 *	TDTABNM		Name of the temporary device table (in the
80 *			directory of the existing table)
81 *	TDTABNMLN	Number of characters added to the directory
82 *			name -- the length of the device table temp name
83 */
84
85#define	TDTABNM		"%sdevtab.%6.6d"
86#define	TDTABNMLN	13
87
88
89/*
90 * Static functions
91 *	strcatesc	Copies a character-string from one place to another
92 *			escaping the appropriate characters
93 *	lkdevtab	Locks the device table
94 *	unlkdevtab	Unlocks the device table
95 *	mkdevtabent	Builds a device-table entry from the alias and the
96 *			list of attr=val pairs given
97 *	opennewdevtab	Opens a new device table (as a temp file)
98 *	mknewdevtab	Makes the temp device table the new devtab
99 *	rmnewdevtab	Remove the temporary device table and free space
100 *			allocated to the filename of that file.
101 */
102
103static	char			*strcatesc(char *, char *);
104static	int			lkdevtab(char *, short);
105static	int			unlkdevtab(void);
106static	struct devtabent	*mkdevtabent(char *, char **);
107static	FILE			*opennewdevtab(char **);
108static	int			mknewdevtab(char *);
109static	int			rmnewdevtab(char *);
110
111/*
112 * char *strcatesc(p, q)
113 *	char   *p
114 *	char   *q
115 *
116 *	Write the character-string pointed to by "q" to the place
117 *	pointed to by "p", escaping those characters in "q" found in the
118 *	string "DTAB_ESCS" by preceding them with '\\'.  Return a pointer to
119 *	the byte beyond the last character written to "p".
120 *
121 *  Arguments:
122 *	p		The place to begin writing to
123 *	q		The string to write
124 *
125 *  Returns:  char *
126 *	The address of the byte beyond the last character written into "p"
127 */
128
129static char *
130strcatesc(
131	char   *p,		/* Place to write to */
132	char   *q)		/* Thing to write */
133{
134	while (*q) {
135	    if (strchr(DTAB_ESCS, *q)) *p++ = '\\';
136	    *p++ = *q++;
137	}
138	return (p);
139}
140
141/*
142 * FILE *opennewdevtab(pname)
143 *	char   **pname
144 *
145 *	Generates a temporary device-table name from the existing
146 *	device table name (in the same directory) and opens that
147 *	file for writing.  It puts a pointer to the malloc()ed space
148 *	containing the temp device table's name at the place referenced
149 *	by <pname>.
150 *
151 *  Arguments:
152 *	pname	Pointer to the char * to contain the address of the name
153 *		of the temporary file
154 *
155 *  Returns:  FILE *
156 *	A pointer to the opened stream or (FILE *) NULL if an error occurred.
157 *	If an error occurred, "errno" will be set to reflect the problem.
158 */
159
160static FILE *
161opennewdevtab(char  **pname)		/* A(ptr to temp filename's path) */
162{
163	char   *oldname;		/* Ptr to the device-table's name */
164	char   *buf;			/* Ptr to the temp file's name */
165	char   *dirname;		/* Directory containing devtab */
166	char   *p;			/* Ptr to last '/' in devtab name */
167	int    fd;			/* Opened file descriptor */
168	FILE   *fp;			/* Opened file pointer */
169	struct stat64	sbuf;		/* stat buf for old devtab file */
170
171	fp = NULL;
172	if (oldname = _devtabpath()) {
173	/*
174	 * It is possible for us to have sufficient permissions to create
175	 * the new file without having sufficient permissions to write the
176	 * original devtab file.  For consistency with the operations which
177	 * modify the original file by writing it directly we require write
178	 * permissions for the original file in order to make a new one.
179	 */
180	    if ((fd = open(oldname, O_WRONLY)) == -1)
181		return (NULL);
182
183	    if (fstat64(fd, &sbuf) == -1) {
184		(void) close(fd);
185		return (NULL);
186	    }
187	    (void) close(fd);
188
189	    if (p = strrchr(oldname, '/')) {
190		*(p+1) = '\0';
191		dirname = oldname;
192	    } else dirname = "./";
193	    if (buf = malloc(TDTABNMLN + strlen(dirname) + 1)) {
194
195		/*
196		 * Build the name of the temp device table and open the
197		 * file.  We must reset the owner, group and perms to those
198		 * of the original devtab file.
199		 */
200		(void) sprintf(buf, TDTABNM, dirname, getpid());
201		if (fp = fopen(buf, "w")) {
202			*pname = buf;
203			(void) fchmod(fileno(fp), sbuf.st_mode & 0777);
204			(void) fchown(fileno(fp), sbuf.st_uid, sbuf.st_gid);
205		} else {
206			free(buf);
207		}
208	    }
209
210	/*
211	 *
212	 * Free the space containing the device table's name.
213	 */
214	    free(oldname);
215	}
216
217	/* Finished.  Return what we've got */
218	return (fp);
219}
220
221/*
222 *  int rmnewdevtab(tempname)
223 *	char   *tempname
224 *
225 *	Unlink the temp device table and free the memory allocated to
226 *	contain the name of that file
227 *
228 *  Arguments:
229 *	tempname	Name of the temporary file
230 *
231 *  Returns: int
232 *	TRUE if successful, FALSE otherwise
233 */
234
235static int
236rmnewdevtab(char *tempname)	/* Filename of new device table */
237{
238	int	noerr;		/* Flag, TRUE if no error, FALSE otherwise */
239
240	/* Unlink the file */
241	noerr = (unlink(tempname) == 0);
242
243	/* Free the space allocated to the filename */
244	free(tempname);
245
246	/* Return success indicator */
247	return (noerr);
248}
249
250/*
251 *  int mknewdevtab(tempname)
252 *	char   *tempname
253 *
254 *	Make the temporary device-table the new system device table
255 *
256 *  Arguments:
257 *	tempname	Name of the temporary file
258 *
259 *  Returns:  int
260 *	TRUE if successful, FALSE otherwise
261 *
262 *  Notes:
263 *	- Need to use rename() someday instead of link()/unlink()
264 *	- This code is somewhat ineffecient in that asks for the name
265 *	  of the device-table more than once.  Done so that we don't
266 *	  have to manage that space, but this may be somewhat lazy.
267 */
268
269static int
270mknewdevtab(char   *tempname)		/* Ptr to name of temp dev tab */
271{
272	char   *devtabname;		/* Ptr to the device table's name */
273	int	noerr;			/* FLAG, TRUE if all's well */
274
275	/* Get the device table's pathname */
276	if (devtabname = _devtabpath()) {
277
278	    /* Unlink the existing file */
279	    if (unlink(devtabname) == 0) {
280
281		/* Make the temp file the real device table */
282		noerr = (link(tempname, devtabname) == 0) ? TRUE : FALSE;
283
284		/* Remove the temp file (and resources) */
285		if (noerr) (void) rmnewdevtab(tempname);
286
287	    } else noerr = FALSE;	/* unlink() failed */
288
289	    /* Free the device table's name */
290	    free(devtabname);
291
292	} else noerr = FALSE; 	/* devtabpath() failed */
293
294	/* Finished.  Return success indicator */
295	return (noerr);
296}
297
298/*
299 * static int lkdevtab(o_mode, lktype)
300 *	char   *o_mode
301 *	short	lktype
302 *
303 *	Lock the device table for writing.  If it isn't available, it waits
304 *	until it is.
305 *
306 *  Arguments:
307 *	o_mode	The open() mode to use when opening the device table
308 *	lktype	The type of lock to apply
309 *
310 *  Returns:  int
311 *	TRUE if successful, FALSE with errno set otherwise
312 */
313
314static int
315lkdevtab(
316	char   *o_mode,				/* Open mode */
317	short	lktype)				/* Lock type */
318{
319	/* Automatic data */
320	struct flock	lockinfo;		/* File locking structure */
321	int		noerr;			/* FLAG, TRUE if no error */
322	int		olderrno;		/* Old value of "errno" */
323
324
325	/* Close the device table (if it's open) */
326	_enddevtab();
327
328	/* Open the device table for read/append */
329	noerr = TRUE;
330	if (_opendevtab(o_mode)) {
331
332	/*
333	 * Lock the device table (for writing).  If it's not
334	 * available, wait until it is, then close and open the
335	 * table (modify and delete change the table!) and try
336	 * to lock it again
337	 */
338
339	    /* Build the locking structure */
340	    lockinfo.l_type = lktype;
341	    lockinfo.l_whence = 0;
342	    lockinfo.l_start = 0L;
343	    lockinfo.l_len = 0L;
344	    olderrno = errno;
345
346	    /* Keep on going until we lock the file or an error happens */
347	    while ((fcntl(fileno(oam_devtab), F_SETLK, &lockinfo) == -1) &&
348		!noerr) {
349		if (errno == EACCES) {
350		    if (fcntl(fileno(oam_devtab), F_SETLKW, &lockinfo) == -1)
351			noerr = FALSE;
352		    else {
353			/* Reopen the file (maybe it's moved?) */
354			_enddevtab();
355			if (!_opendevtab(o_mode)) noerr = FALSE;
356			else errno = olderrno;
357		    }
358		} else noerr = FALSE;
359	    }
360
361	    if (!noerr) _enddevtab();  /* Don't keep open if in error */
362
363	} else noerr = FALSE;
364
365	/* Done */
366	return (noerr);
367}
368
369/*
370 * int unlkdevtab()
371 *
372 *	Unlock the locked device table.
373 *
374 *  Arguments:  None
375 *
376 *  Returns:  int
377 *	Whatever fcntl() returns...
378 */
379
380static int
381unlkdevtab(void)
382{
383	/* Automatic data */
384	struct flock	lockinfo;		/* Locking structure */
385	int		noerr;			/* FLAG, TRUE if all's well */
386
387	/* Build the locking structure */
388	lockinfo.l_type = F_UNLCK;		/* Lock type */
389	lockinfo.l_whence = 0;			/* Count from top of file */
390	lockinfo.l_start = 0L;			/* From beginning */
391	lockinfo.l_len = 0L;			/* Length of locked data */
392
393	/* Unlock it */
394	noerr = (fcntl(fileno(oam_devtab), F_SETLK, &lockinfo) != -1);
395	_enddevtab();
396
397	/* Finished */
398	return (noerr);
399}
400
401/*
402 * struct devtabent *mkdevtabent(alias, attrlist)
403 *	char   *alias
404 *	char  **attrlist
405 *
406 *	This function builds a struct devtabent structure describing the
407 *	alias <alias> using the information in the attribute list <attrlist>.
408 *	The <attrlist> contains data of the form attr=value where attr is
409 *	the name of an attribute and value is the value of that attribute.
410 *
411 *  Arguments:
412 *	alias		The alias being added to the device table
413 *	attrlist	The attributes and values for that alias
414 *
415 *  Returns:  struct devtabent *
416 *	A completed struct devtabent structure containing the description
417 *	of the alias.  The structure, and all of the data in the structure
418 *	are each in space allocated using the malloc() function and should
419 *	be freed using the free() function (or the _freedevtabent() function).
420 *
421 *  Errors:
422 *	EINVAL	If "alias" is used as an attribute in an attr=val pair
423 *	EAGAIN	If an attribute is specified more than once
424 */
425
426static struct devtabent *
427mkdevtabent(
428	char   *alias,		/* Alias of entry */
429	char  **attrlist)	/* Attributes of new entry */
430{
431	/* Automatic data */
432	struct devtabent	*devtabent;	/* * to struct we're making */
433	struct attrval		*prevattrval;	/* * to prev attr/val struct */
434	struct attrval		*attrval;	/* * to current struct */
435	char			**pp;		/* Ptr into list of ptrs */
436	char			*peq;		/* Ptr to '=' in string */
437	char			*val;		/* Ptr to space for value */
438	char			*name;		/* Ptr to space for name */
439	ssize_t			len;		/* Length of name */
440	int			noerr;		/* TRUE if all's well */
441	int			found;		/* TRUE the attr is found */
442
443
444	/* No problems (yet) */
445	noerr = TRUE;
446
447	/* Get space for the structure */
448	if (devtabent = malloc(sizeof (struct devtabent))) {
449
450	    /* Fill in default values */
451	    if (devtabent->alias = malloc(strlen(alias)+1)) {
452
453		(void) strcpy(devtabent->alias, alias);		/* alias */
454		devtabent->comment = FALSE;			/* data rec */
455		devtabent->cdevice = NULL;			/* cdevice */
456		devtabent->bdevice = NULL;			/* bdevice */
457		devtabent->pathname = NULL;			/* pathname */
458		devtabent->attrstr = NULL;			/* string */
459		devtabent->attrlist = NULL;			/* attr list */
460
461		/* Add attributes to the structure */
462		prevattrval = NULL;
463		if ((pp = attrlist) != NULL)
464		    while (*pp && noerr) {
465
466		    /* Valid attr=value pair? */
467		    if (((peq = strchr(*pp, '=')) != NULL) &&
468			((len = peq - *pp) > 0)) {
469
470			/* Get space for the value */
471			if (val = malloc(strlen(peq))) {
472			    (void) strcpy(val, peq+1);		/* Copy it */
473
474			    /* Get space for attribute name */
475			    if (name = malloc((size_t)(len + 1))) {
476				(void) strncpy(name, *pp, (size_t)len);
477				*(name+len) = '\0';
478
479				/* Specifying the alias?  If so, ERROR */
480				if (strcmp(name, DTAB_ALIAS) == 0) {
481				    noerr = FALSE;
482				    free(name);
483				    free(val);
484				    errno = EINVAL;
485				}
486
487				/* Specifying the char device path? */
488				else if (strcmp(name, DTAB_CDEVICE) == 0) {
489				    if (!devtabent->cdevice) {
490					if (val[0] != '/') {
491						noerr = FALSE;
492						free(name);
493						free(val);
494						errno = ENXIO;
495					} else {
496						devtabent->cdevice = val;
497						free(name);
498					}
499				    } else {
500					noerr = FALSE;
501					free(name);
502					free(val);
503					errno = EAGAIN;
504				    }
505				}
506
507				/* Specifying the block device path? */
508				else if (strcmp(name, DTAB_BDEVICE) == 0) {
509				    if (!devtabent->bdevice) {
510					if (val[0] != '/') {
511						noerr = FALSE;
512						free(name);
513						free(val);
514						errno = ENXIO;
515					} else {
516						devtabent->bdevice = val;
517						free(name);
518					}
519				    } else {
520					noerr = FALSE;
521					free(name);
522					free(val);
523					errno = EAGAIN;
524				    }
525				}
526
527				/* Specifying the pathname (generic)? */
528				else if (strcmp(name, DTAB_PATHNAME) == 0) {
529				    if (!devtabent->pathname) {
530					if (val[0] != '/') {
531						noerr = FALSE;
532						free(name);
533						free(val);
534						errno = ENXIO;
535					} else {
536						devtabent->pathname = val;
537						free(name);
538					}
539				    } else {
540					noerr = FALSE;
541					free(name);
542					free(val);
543					errno = EAGAIN;
544				    }
545				}
546
547				/* Some other attribute */
548				else {
549				    found = FALSE;
550				    if ((attrval = devtabent->attrlist) != NULL)
551					do {
552					    if (strcmp(attrval->attr,
553						name) == 0) {
554
555						noerr = FALSE;
556						free(name);
557						free(val);
558						errno = EAGAIN;
559					    }
560					} while (!found && noerr &&
561					    (attrval = attrval->next));
562
563				    if (!found && noerr) {
564
565					/* Get space for attr/val structure */
566					if (attrval =
567					    malloc(sizeof (struct attrval))) {
568
569					    /* Fill attr/val structure */
570					    attrval->attr = name;
571					    attrval->val = val;
572					    attrval->next = NULL;
573
574					/*
575					 * Link into the list of attributes
576					 */
577					    if (prevattrval)
578						prevattrval->next = attrval;
579					    else devtabent->attrlist = attrval;
580					    prevattrval = attrval;
581
582					} else {
583					    /* malloc() for attrval failed */
584					    noerr = FALSE;
585					    free(name);
586					    free(val);
587					}
588				    }
589				}   /* End else (some other attribute) */
590
591			    } else { 	/* malloc() for attribute name failed */
592				noerr = FALSE;
593				free(val);
594			    }
595
596			} else noerr = FALSE;	/* Malloc() for "val" failed */
597
598			/* If we saw an error, free structure, returning NULL */
599			if (!noerr) {
600			    _freedevtabent(devtabent);
601			    devtabent = NULL;
602			}
603
604		    } 	/* Ignore invalid attr=val pair */
605
606		    if (noerr) pp++;
607
608		}   /* End attribute processing loop */
609
610	    } else {	/* malloc() failed */
611		free(devtabent);
612		devtabent = NULL;
613	    }
614	}
615
616	/* Finished */
617	return (devtabent);
618}
619
620/*
621 * int _putdevtabrec(stream, rec)
622 *	FILE		       *stream
623 *	struct devtabent       *rec
624 *
625 *	Write a device table record containing the information in the struct
626 *	devtab structure <rec> to the current position of the standard I/O
627 *	stream <stream>.
628 *
629 *  Arguments:
630 *	stream		The stream to write to
631 *	rec		The structure containing the information to write
632 *
633 *  Returns:  int
634 *	The number of characters written or EOF if there was some error.
635 */
636
637int
638_putdevtabrec(
639	FILE			*stream,	/* Stream to which to write */
640	struct devtabent	*rec)		/* Record to write */
641{
642	/* Automatic Data */
643	struct attrval		*attrval;	/* Ptr to attr/val pair */
644	char			*buf;		/* Allocated buffer */
645	char			*p;		/* Temp char pointer */
646	int			count;		/* Number of chars written */
647	size_t			size = 0;	/* Size of needed buffer */
648
649
650	/* Comment or data record? */
651	if (rec->comment) {
652
653	/*
654	 * Record is a comment
655	 */
656
657	    /* Copy (escaping chars) record into temp buffer */
658	    size = (strlen(rec->attrstr)*2)+1;		/* Max rec size */
659	    if (buf = malloc(size+1)) {
660		/* Alloc space */
661		p = strcatesc(buf, rec->attrstr);	/* Copy "escaped" */
662		*(p-2) = '\n';				/* Unescape last \n */
663		*(p-1) = '\0';				/* Terminate string */
664
665		/* Write the record */
666		count = fputs(buf, stream);
667		free(buf);
668
669	    } else count = EOF;  /* malloc() failed */
670	}
671
672	else {
673
674		/*
675		 * Record is a data record
676		 */
677
678		/*
679		 * Figure out maximum amount of space you're going to need.
680		 * (Assume every escapable character is escaped to determine the
681		 * maximum size needed)
682		 */
683
684	    if (rec->cdevice)
685		size += (strlen(rec->cdevice)*2) + 1;	/* cdevice: */
686	    if (rec->bdevice)
687		size += (strlen(rec->bdevice)*2) + 1;	/* bdevice: */
688	    if (rec->pathname)
689		size += (strlen(rec->pathname)*2) + 1;	/* pathname: */
690	    if ((attrval = rec->attrlist) != NULL) do {	/* Attributes */
691		if (attrval->attr)
692			size += (strlen(attrval->attr)*2);	    /* attr */
693		if (attrval->val) {
694			/* val & '="" ' or val & '=""\n' */
695			size += (strlen(attrval->val)*2) +4;
696		}
697	    } while ((attrval = attrval->next) != NULL);    /* Next attr/val */
698	    else size++;		/* Else make room for trailing '\n' */
699
700	    /* Alloc space for "escaped" record */
701	    if (buf = malloc(size+1)) {
702
703		/* Initializations */
704		p = buf;
705
706		/* Write the alias ("alias" attribute) */
707		p = strcatesc(p, rec->alias);
708		*p++ = ':';
709
710		/* Write the character device ("cdevice" attribute) */
711		if (rec->cdevice) p = strcatesc(p, rec->cdevice);
712		*p++ = ':';
713
714		/* Write the block device ("bdevice" attribute) */
715		if (rec->bdevice) p = strcatesc(p, rec->bdevice);
716		*p++ = ':';
717
718		/* Write the pathname ("pathname" attribute) */
719		if (rec->pathname) p = strcatesc(p, rec->pathname);
720		*p++ = ':';
721
722		/* Write the rest of the attributes */
723		if ((attrval = rec->attrlist) != NULL)
724		    do {
725			p = strcatesc(p, attrval->attr);
726			*p++ = '=';
727			*p++ = '"';
728			p = strcatesc(p, attrval->val);
729			*p++ = '"';
730			if ((attrval = attrval->next) != NULL)
731			    *p++ = ' ';
732		    } while (attrval);
733
734		/* Terminate the record */
735		*p++ = '\n';
736		*p = '\0';
737
738		/* Write the record */
739		count = fputs(buf, stream);
740		free(buf);
741	    } else count = EOF;  /* malloc() failed */
742	}
743
744	/* Finished */
745	return (count);
746}
747
748/*
749 *  int _adddevtabrec(alias, attrval)
750 *	char   *alias
751 *	char  **attrval
752 *
753 *	This function adds a record to the device table.  That record will
754 *	have the alias <alias> and will have the attributes described in
755 *	the list referenced by <attrval>.
756 *
757 *	It always adds the record to the end of the table.
758 *
759 *  Arguments:
760 *	alias		The alias of the device whose description is being
761 *			added to the device table.
762 *	attrval		The pointer to the first item of a list of attributes
763 *			defining the device whose description is being added.
764 *			(This value may be (char **) NULL).
765 *
766 *  Returns:  int
767 *	TRUE if successful, FALSE with errno set otherwise.
768 */
769
770int
771_adddevtabrec(
772	char   *alias,			/* Alias to add to the device table */
773	char  **attrval)		/* Attributes for that device */
774{
775	/* Automatic data */
776	struct devtabent	*devtabent;	/* Ptr to dev tab entry */
777	int			olderrno;	/* Errno on entry */
778	int			noerr;		/* FLAG, TRUE if all's well */
779
780	/* Validate the device alias.  Error (EINVAL) if it's not valid */
781	if (!_validalias(alias)) {
782	    errno = EINVAL;
783	    return (FALSE);
784	}
785
786	/*
787	 * Lock the device table.  This only returns if the table is locked or
788	 * some error occurred.  It waits until the table is available.
789	 */
790	if (!lkdevtab("a+", F_WRLCK))
791		return (FALSE);
792
793	/* Make sure that the alias isn't already in the table */
794	noerr = TRUE;
795	olderrno = errno;
796	if (devtabent = _getdevrec(alias)) {
797
798	    /* The alias is already in the table */
799	    _freedevtabent(devtabent);		/* Free device table info */
800	    errno = EEXIST;			/* Set errno, entry exists */
801	    noerr = FALSE;			/* All's not well */
802	} else if ((errno == ENOENT) || (errno == ENODEV)) {
803
804	    /* The alias wasn't in the table or there wasn't a table. */
805
806	    errno = olderrno;			/* Reset errno */
807
808	    /* Build a struct devtabent that describes the new alias */
809	    if (devtabent = mkdevtabent(alias, attrval)) {
810
811		/* Position to the end of the existing table */
812		if (fseek(oam_devtab, 0, SEEK_END) == 0)
813
814		    /* Write the new entry */
815		    noerr = (_putdevtabrec(oam_devtab, devtabent) != EOF);
816
817		/* Free the info we just wrote */
818		_freedevtabent(devtabent);
819
820	    } else noerr = FALSE;	/* mkdevtabent() failed */
821	} else noerr = FALSE;		/* Some odd error, _devtab */
822
823	/* Unlock and close the device table */
824	(void) unlkdevtab();
825
826	/* Fini */
827	return (noerr);
828}
829
830/*
831 * int _moddevtabrec(device, attrval)
832 *	char   *device
833 *	char  **attrval
834 *
835 *	This function modifies the description for the specified device
836 *	so that it has the attributes and values as specified in the
837 *	given list.
838 *
839 *  Arguments:
840 *	device		The name of the device whose description
841 *			is being modified
842 *	attrval		The first attr/val value in the list (attr=val) of
843 *			the attributes that are to change
844 *
845 *  Returns:  int
846 *	TRUE if all went well, FALSE with errno set otherwise
847 */
848
849int
850_moddevtabrec(
851	char   *device,			/* Device to modify */
852	char  **attrval)		/* Attributes to add or change */
853{
854	/* Automatic data */
855	FILE			*fd;	/* File ptr, new device table */
856	struct devtabent	*ent;	/* Device's current description */
857	struct devtabent	*chg;	/* Changes to make to description */
858	struct attrval		*new;	/* New attribute/value desc */
859	struct attrval		*old;	/* Old attribute/value desc */
860	struct attrval		*newnew; /* Next "new" value to look at */
861	struct attrval		*prevnew; /* Previous item in the 'new' list */
862	char			*tname;	/* name of temp devtab file */
863	int			noerr;	/* FLAG, TRUE if all's well */
864	int			found;	/* FLAG, TRUE if attr found for dev */
865
866	/* Lock the device table */
867	if (!lkdevtab("r", F_WRLCK))
868		return (FALSE);
869
870	/* No problems (so far) */
871	noerr = TRUE;
872
873	/* Get the entry to modify */
874	if (ent = _getdevrec(device)) {
875
876	    /* Build a structure describing the changes */
877	    if (chg = mkdevtabent(device, attrval)) {
878
879		/* If the "cdevice" field is specified, change it */
880		if (chg->cdevice) {
881		    if (ent->cdevice) free(ent->cdevice);
882		    ent->cdevice = chg->cdevice;
883		    chg->cdevice = NULL;
884		}
885
886		/* If the "bdevice" field is specified, change it */
887		if (chg->bdevice) {
888		    if (ent->bdevice) free(ent->bdevice);
889		    ent->bdevice = chg->bdevice;
890		    chg->bdevice = NULL;
891		}
892
893		/* If the "pathname" field is specified, change it */
894		if (chg->pathname) {
895		    if (ent->pathname) free(ent->pathname);
896		    ent->pathname = chg->pathname;
897		    chg->pathname = NULL;
898		}
899
900		/* Change the other attributes (if any) */
901		if (ent->attrlist) {
902		    prevnew = NULL;
903		    if ((new = chg->attrlist) != NULL) do {
904
905			found = FALSE;
906			for (old = ent->attrlist; !found && old;
907			    old = old->next) {
908			    if (strcmp(old->attr, new->attr) == 0) {
909				found = TRUE;
910				free(old->val);
911				old->val = new->val;
912				new->val = NULL;
913			    }
914			}   /* Loop through the existing attribute list */
915
916			/*
917			 * If the attribute wasn't found, add it to the list
918			 * of attributes for the device.  If it was found, just
919			 * bump to the next one and look for it
920			 */
921
922			if (!found) {
923
924			/*
925			 * Not found.  Move attr/val description to the
926			 * device's list of attributes
927			 */
928
929			    if (prevnew) prevnew->next = new->next;
930			    else chg->attrlist = new->next;
931			    newnew = new->next;
932			    new->next = ent->attrlist;
933			    ent->attrlist = new;
934			    new = newnew;
935			} else {
936
937			    /* Attribute changed, bump to the next one */
938			    prevnew = new;
939			    new = new->next;
940			}
941		    } while (new);  /* Loop for each attr to add or modify */
942
943		} else {
944
945		    /* Device had no attributes -- add entire list */
946		    ent->attrlist = chg->attrlist;
947		    chg->attrlist = NULL;
948		}
949
950		/* Free the structure containing the changes */
951		_freedevtabent(chg);
952
953	    } else noerr = FALSE;   /* Couldn't build changes struct */
954
955	    /* If there hasn't been an error (so far), write the new record */
956	    if (noerr) {
957
958		/* Open the new device table */
959		if (fd = opennewdevtab(&tname)) {
960
961		/*
962		 * For each entry in the existing table, write that entry
963		 * to the new table.  If the entry is the one being
964		 * modified, write the modified entry instead of the
965		 * original entry.
966		 */
967
968		    _setdevtab();		/* Rewind existing table */
969		    chg = ent;			/* Remember new record */
970		    while (((ent = _getdevtabent()) != NULL) && noerr) {
971			if (ent->entryno != chg->entryno)
972			    noerr = _putdevtabrec(fd, ent) != EOF;
973			else noerr = _putdevtabrec(fd, chg) != EOF;
974			_freedevtabent(ent);
975		    }
976
977		/*
978		 * If we successfully generated the new table, make it the
979		 * new system device table.  Otherwise, just remove the
980		 * temporary file we've created.
981		 */
982
983		    if (noerr) {
984			(void) fclose(fd);
985			noerr = mknewdevtab(tname);
986		    } else {
987			(void) fclose(fd);
988			(void) rmnewdevtab(tname);
989		    }
990
991		    /* Free the changed device structure */
992		    _freedevtabent(chg);
993
994		}   /* if (_opennewdevtab()) */
995		else noerr = FALSE;
996
997	    } else _freedevtabent(ent);  /* if (noerr) */
998
999	} else noerr = FALSE;	/* Device not found? */
1000
1001	/* Finished.  Unlock the device table and quit */
1002	(void) unlkdevtab();
1003	return (noerr);
1004}
1005
1006/*
1007 * int _rmdevtabrec(device)
1008 *	char   *device
1009 *
1010 *	This function removes the record in the device table for the specified
1011 *	device.
1012 *
1013 *  Arguments:
1014 *	device	The device (alias, cdevice, bdevice, pathname, or link to one)
1015 *		whose entry is to be removed
1016 *
1017 *  Returns:  int
1018 *	Success indicator:  TRUE if successful, FALSE with errno set otherwise.
1019 */
1020
1021int
1022_rmdevtabrec(char *device)		/* Device to remove */
1023{
1024	struct devtabent	*rment;
1025	struct devtabent	*devtabent;
1026	char			*tempname;
1027	FILE			*fd;
1028	int			noerr;
1029
1030	if (!lkdevtab("r", F_WRLCK))
1031		return (FALSE);
1032	noerr = TRUE;
1033	if (rment = _getdevrec(device)) {
1034	    if (fd = opennewdevtab(&tempname)) {
1035		_setdevtab();
1036		while (((devtabent = _getdevtabent()) != NULL) && noerr) {
1037		    if (devtabent->entryno != rment->entryno)
1038			noerr = _putdevtabrec(fd, devtabent) != EOF;
1039		    _freedevtabent(devtabent);
1040		}
1041		if (noerr) {
1042		    (void) fclose(fd);
1043		    noerr = mknewdevtab(tempname);
1044		} else {
1045		    (void) fclose(fd);
1046		    (void) rmnewdevtab(tempname);
1047		}
1048	    } else noerr = FALSE;
1049	    _freedevtabent(rment);
1050	} else noerr = FALSE;
1051	(void) unlkdevtab();
1052	return (noerr);
1053}
1054
1055/*
1056 * int _rmdevtabattrs(device, attributes, notfounds)
1057 *	char   *device
1058 *	char  **attributes
1059 *	char ***notfounds
1060 *
1061 *	Remove the specified attributes from the specified device.  The
1062 *	device is specified by <device>, <attributes> is the address of
1063 *	the first char * in the list of char * pointing to the attributes
1064 *	to remove from the device, and <notfounds> is the address of a
1065 *	char ** to put the address of the first element in the malloc()ed
1066 *	list of (char *) pointing to requested attributes that were not
1067 *	defined for the device <device>.
1068 *
1069 *  Arguments:
1070 *	device		The device from which attributes are to be removed
1071 *	attributes	The address of the first element in the list of
1072 *			attributes to remove.  This list is terminated by
1073 *			(char *) NULL.
1074 *	notfounds	The place to put the address of the list of addresses
1075 *			referencing the requested attributes that are not
1076 *			defined for the specified device.
1077 *
1078 *  Returns: int
1079 *	TRUE if successful, FALSE with errno set otherwise.
1080 *
1081 *  Notes:
1082 *    -	"alias" may not be undefined
1083 *    - "cdevice", "bdevice", and "pathname" are made "null", not really
1084 *	undefined
1085 */
1086
1087int
1088_rmdevtabattrs(
1089	char   *device,			/* Device to modify */
1090	char  **attributes,		/* Attributes to remove */
1091	char ***notfounds)		/* Attributes req'd but not found */
1092{
1093	/* Automatics */
1094	char			**pnxt;		/* Ptr to next attribute */
1095	char			**pp;		/* Ptr to current attr name */
1096	struct devtabent	*modent;	/* Entry being modified */
1097	struct devtabent	*devtabent;	/* Entry being copied */
1098	struct attrval		*attrval;	/* Ptr to attr/val desc */
1099	struct attrval		*prevattrval;	/* Ptr to prev attr/val */
1100	FILE			*fd;		/* File desc, temp file */
1101	char			*tempname;	/* Name of temp file */
1102	int			nattrs;		/* Number of attrs to remove */
1103	int			nobaderr;	/* TRUE if no fatal error */
1104	int			noerr;		/* TRUE if no non-fatal error */
1105	int			found;		/* TRUE if attribute found */
1106	int			nonotfounds;	/* TRUE if no attrs not fount */
1107
1108
1109	/* Initializations */
1110	nobaderr = TRUE;
1111	noerr = TRUE;
1112
1113	/* Count attributes to remove -- make sure "alias" isn't specified */
1114	for (pp = attributes, nattrs = 0; *pp; pp++, nattrs++)
1115	    if (strcmp(*pp, DTAB_ALIAS) == 0) {
1116		*notfounds = NULL;
1117		errno = EINVAL;
1118		return (FALSE);
1119	    }
1120
1121	/* Lock the device table */
1122	if (!lkdevtab("r", F_WRLCK))
1123		return (FALSE);
1124
1125	/* Is there a record for the requested device? */
1126	if (modent = _getdevrec(device)) {
1127
1128	    /* Record found.  Try to modify it */
1129	    nonotfounds = TRUE;
1130
1131	    /* For each of the attributes in the attribute list ... */
1132	    for (pp = attributes; nobaderr && *pp; pp++) {
1133
1134		/*
1135		 * Modify the device description, removing the requested
1136		 * attributes from the structure
1137		 */
1138
1139		found = FALSE;				/* Not found yet */
1140
1141		/* If it's the "cdevice" attribute, make it a null-string */
1142		if (strcmp(*pp, DTAB_CDEVICE) == 0) {
1143		    if (modent->cdevice) {
1144			free(modent->cdevice);
1145			modent->cdevice = NULL;
1146		    }
1147		    found = TRUE;
1148		}
1149
1150		/* If it's the "bdevice" attribute, make it a null-string */
1151		else if (strcmp(*pp, DTAB_BDEVICE) == 0) {
1152		    if (modent->bdevice) {
1153			free(modent->bdevice);
1154			modent->bdevice = NULL;
1155		    }
1156		    found = TRUE;
1157		}
1158
1159		/* If it's the "pathname" attribute, make it a null-string */
1160		else if (strcmp(*pp, DTAB_PATHNAME) == 0) {
1161		    if (modent->pathname) {
1162			free(modent->pathname);
1163			modent->pathname = NULL;
1164		    }
1165		    found = TRUE;
1166		}
1167
1168		/* Must be one of the other "auxilliary" attributes */
1169		else {
1170
1171		    /* Search the attribute list for the attribute */
1172		    prevattrval = NULL;
1173		    if ((attrval = modent->attrlist) != NULL) do {
1174			if (strcmp(*pp, attrval->attr) == 0) {
1175
1176			    /* Found.  Remove from attribute list */
1177			    found = TRUE;
1178			    free(attrval->attr);
1179			    free(attrval->val);
1180			    if (prevattrval) {
1181				prevattrval->next = attrval->next;
1182				free(attrval);
1183				attrval = prevattrval->next;
1184			    } else {
1185				modent->attrlist = attrval->next;
1186				free(attrval);
1187				attrval = modent->attrlist;
1188			    }
1189			} else {
1190			    prevattrval = attrval;	/* Advance to next */
1191			    attrval = attrval->next;
1192			}
1193		    } while (!found && attrval);
1194
1195		}   /* End attribute search loop */
1196
1197		/*
1198		 * If the requested attribute wasn't defined for the device,
1199		 * put it in the list of attributes not found
1200		 */
1201
1202		if (!found) {
1203
1204			/*
1205			 * If there's no list (yet), alloc enough space for
1206			 * the list
1207			 */
1208
1209		    if (nonotfounds)
1210			if (*notfounds = malloc(sizeof (char **)*(nattrs+1))) {
1211
1212			    /* List allocated -- put in the first entry */
1213			    nonotfounds = FALSE;
1214			    pnxt = *notfounds;
1215			    if (*pnxt = malloc(strlen(*pp)+1)) {
1216				errno = EINVAL;
1217				noerr = FALSE;
1218				(void) strcpy(*pnxt++, *pp);
1219			    } else {
1220				/* malloc() failed, free list */
1221				free(*notfounds);
1222				*notfounds = NULL;
1223				nonotfounds = TRUE;
1224				nobaderr = FALSE;
1225			    }
1226
1227			} else nobaderr = FALSE;  /* malloc() failed */
1228
1229		    else {
1230			/* Already a list, add this attribute to it */
1231			if (*pnxt = malloc(strlen(*pp)+1))
1232			    (void) strcpy(*pnxt++, *pp);
1233			else {
1234			    /* Out of memory, clean up */
1235			    for (pnxt = *notfounds; *pnxt; pnxt++)
1236				free(*pnxt);
1237			    free(*notfounds);
1238			    *notfounds = NULL;
1239			    nonotfounds = TRUE;
1240			    nobaderr = FALSE;
1241			}
1242		    }
1243
1244		}    /* end if (!found) */
1245
1246		/* Terminate the not-found list */
1247		if (!nonotfounds) *pnxt = NULL;
1248
1249	    }	/* end (for each attribute in attribute list) loop */
1250
1251
1252		/*
1253		 * If we haven't seen any problems so far,
1254		 * write the new device table
1255		 */
1256
1257	    if (nobaderr) {
1258
1259		/* Open the new device table */
1260		if (fd = opennewdevtab(&tempname)) {
1261
1262		/*
1263		 * For each entry in the existing table, write that entry
1264		 * to the new table.  If the entry is the one being
1265		 * modified, write the modified entry instead of the
1266		 * original entry.
1267		 */
1268
1269		    _setdevtab();		/* Rewind existing table */
1270		    while (((devtabent = _getdevtabent()) != NULL) &&
1271			nobaderr) {
1272
1273			if (devtabent->entryno != modent->entryno)
1274			    nobaderr = _putdevtabrec(fd, devtabent) != EOF;
1275			else nobaderr = _putdevtabrec(fd, modent) != EOF;
1276			_freedevtabent(devtabent);
1277		    }
1278
1279		/*
1280		 * If we successfully generated the new table, make it the
1281		 * new system device table.  Otherwise, just remove the
1282		 * temporary file we've created.
1283		 */
1284
1285		    if (nobaderr) {
1286			(void) fclose(fd);
1287			nobaderr = mknewdevtab(tempname);
1288		    } else {
1289			(void) fclose(fd);
1290			(void) rmnewdevtab(tempname);
1291		    }
1292
1293		}   /* if (_opennewdevtab()) */
1294		else nobaderr = FALSE;
1295
1296		/*
1297		 * If there was some error, we need to clean up
1298		 * allocated resources
1299		 */
1300		if (!nobaderr && !nonotfounds) {
1301		    for (pnxt = *notfounds; *pnxt; pnxt++)
1302			free(*pnxt);
1303		    free(*notfounds);
1304		    *notfounds = NULL;
1305		    nonotfounds = TRUE;
1306		}
1307
1308	    }	/* if (nobaderr) */
1309
1310	    /* Free the resources alloc'ed for <device>'s entry */
1311	    _freedevtabent(modent);
1312
1313	} else {
1314	    /* _getdevrec(device) failed */
1315	    nobaderr = FALSE;
1316	    *notfounds = NULL;
1317	}
1318
1319	/* Unlock the device table */
1320	(void) unlkdevtab();
1321
1322	/* We're finished */
1323	return (noerr && nobaderr);
1324}
1325