xref: /illumos-gate/usr/src/lib/libadm/common/putdgrp.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
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  * putdgrp.c
35  *
36  * Global Definitions:
37  *	_putdgrptabrec()	Write a device-group record to a stream
38  *	_rmdgrptabrec()		Remove a device-group table record
39  *	_rmdgrpmems()		Remove specific members from a device group
40  *	_adddgrptabrec()	Add a device-group record to the table
41  */
42 
43 /*
44  *  G L O B A L   R E F E R E N C E S
45  *
46  *	Header Files
47  *	Externals Referenced
48  */
49 
50 /*
51  * Header Files
52  *	<sys/types.h>		UNIX System Data Types
53  *	<stdio.h>		Standard I/O definitions
54  *	<fcntl.h>		Definitions for file control
55  *	<errno.h>		Error handling definitions
56  *	<string.h>		String Handling Definitions
57  *	<unistd.h>		Standard UNIX(r) Definitions
58  *	<devmgmt.h>		Device Management Definitions
59  *	"devtab.h"		Local Device Management Definitions
60  */
61 
62 #include	<sys/types.h>
63 #include	<sys/stat.h>
64 #include	<stdio.h>
65 #include	<fcntl.h>
66 #include	<errno.h>
67 #include	<string.h>
68 #include	<unistd.h>
69 #include	<stdlib.h>
70 #include	<devmgmt.h>
71 #include	"devtab.h"
72 
73 /*
74  *  L O C A L   D E F I N I T I O N S
75  *	TDGTABNM	Name of the temporary device-group table (in the
76  *			directory of the existing table)
77  *	TDGTABNMLN	Number of characters added to the directory
78  *			name -- the length of the device-group table temp name
79  */
80 
81 #define	TDGTABNM	"%sdgroup.%6.6d"
82 #define	TDGTABNMLN	13
83 
84 
85 /*
86  * Static functions
87  *	lkdgrptab	Locks the device-group table
88  *	unlkdgrptab	Unlocks the device-group table
89  *	mkdgrptabent	Builds a device-group table entry from the alias and the
90  *			list of attr=val pairs given
91  *	opennewdgrptab	Opens a new device-group table (as a temp file)
92  *	mknewdgrptab	Makes the temp device-group table the new dgrptab
93  *	rmnewdgrptab	Remove the temporary device-group table and free space
94  *			allocated to the filename of that file.
95  */
96 
97 static	int			lkdgrptab(char *o_mode, short lktype);
98 static	int			unlkdgrptab(void);
99 static	struct dgrptabent	*mkdgrptabent(char *dgroup, char **members);
100 static	FILE			*opennewdgrptab(char **pname);
101 static	int			mknewdgrptab(char *tempname);
102 static	int			rmnewdgrptab(char *tempname);
103 
104 /*
105  * FILE *opennewdgrptab(pname)
106  *	char   **pname
107  *
108  *	Generates a temporary device-group table name from the existing
109  *	device-group table name (in the same directory) and opens that
110  *	file for writing.  It puts a pointer to the malloc()ed space
111  *	containing the temp device-group table's name at the place
112  *	referenced by <pname>.
113  *
114  *  Arguments:
115  *	pname	Pointer to the char * to contain the address of the name
116  *		of the temporary file
117  *
118  *  Returns:  FILE *
119  *	A pointer to the opened stream or (FILE *) NULL if an error occurred.
120  *	If an error occurred, "errno" will be set to reflect the problem.
121  */
122 
123 static FILE *
124 opennewdgrptab(char **pname)	 /* A(ptr to temp filename's path) */
125 {
126 	char   *oldname;		/* Ptr to the dgrptab name */
127 	char   *buf;			/* Ptr to the temp file's name */
128 	char   *dirname;		/* Directory containing dgrptab */
129 	char   *p;			/* Ptr to last '/' in dgrptab name */
130 	int    fd;			/* Opened file descriptor */
131 	FILE   *fp;			/* Opened file pointer */
132 	struct stat64	sbuf;		/* stat buf for old dgrptab file */
133 
134 
135 	/* Initializations */
136 	fp = NULL;
137 
138 	/* Get the name of the device-group table */
139 	if (oldname = _dgrptabpath()) {
140 	/*
141 	 * It is possible for us to have sufficient permissions to create
142 	 * the new file without having sufficient permissions to write the
143 	 * original dgrptab file.  For consistency with the operations which
144 	 * modify the original file by writing it directly we require write
145 	 * permissions for the original file in order to make a new one.
146 	 */
147 	    if ((fd = open(oldname, O_WRONLY)) == -1)
148 		return (NULL);
149 
150 	    if (fstat64(fd, &sbuf) == -1) {
151 		(void) close(fd);
152 		return (NULL);
153 	    }
154 	    (void) close(fd);
155 
156 	    /* Get the directory that the device-group table lives in */
157 	    if (p = strrchr(oldname, '/')) {
158 		*(p+1) = '\0';
159 		dirname = oldname;
160 	    } else
161 		dirname = "./";
162 
163 	    /* Get space for the temp dgrptab pathname */
164 	    if (buf = malloc(TDGTABNMLN+strlen(dirname)+1)) {
165 
166 		/*
167 		 * Build the name of the temp dgrptab and open the
168 		 * file.  We must reset the owner, group and perms to those
169 		 * of the original dgrptab file.
170 		 */
171 		(void) sprintf(buf, TDGTABNM, dirname, getpid());
172 		if (fp = fopen(buf, "w")) {
173 			*pname = buf;
174 			(void) fchmod(fileno(fp), sbuf.st_mode & 0777);
175 			(void) fchown(fileno(fp), sbuf.st_uid, sbuf.st_gid);
176 		} else {
177 			free(buf);
178 		}
179 	    }
180 
181 	    /* Free the space containing the dgrptab's name */
182 	    free(oldname);
183 	}
184 
185 	/* Finished.  Return what we've got */
186 	return (fp);
187 }
188 
189 /*
190  *  int rmnewdgrptab(tempname)
191  *	char   *tempname
192  *
193  *	Unlink the temp dgrptab and free the memory allocated to
194  *	contain the name of that file
195  *
196  *  Arguments:
197  *	tempname	Name of the temporary file
198  *
199  *  Returns: int
200  *	TRUE if successful, FALSE otherwise
201  */
202 
203 static int
204 rmnewdgrptab(char *tempname)
205 {
206 	/* Automatic data */
207 	int	noerr;
208 
209 	/* Unlink the temporary file */
210 	noerr = (unlink(tempname) == 0);
211 	free(tempname);
212 
213 	/* Finished */
214 	return (noerr);
215 }
216 
217 /*
218  *  int mknewdgrptab(tempname)
219  *	char   *tempname
220  *
221  *	Make the temporary device-group table the new system
222  *	device-group table
223  *
224  *  Arguments:
225  *	tempname	Name of the temporary file
226  *
227  *  Returns:  int
228  *	TRUE if successful, FALSE otherwise
229  *
230  *  Notes:
231  *	- Need to use rename() someday instead of link()/unlink()
232  *	- This code is somewhat ineffecient in that asks for the name
233  *	  of the device-group table more than once.  Done so that we don't
234  *	  have to manage that space, but this may be somewhat lazy.
235  */
236 
237 static int
238 mknewdgrptab(char *tempname)		/* Ptr to name of temp dgrp tab */
239 {
240 	char   *dgrpname;		/* Ptr to the dgrptab's name */
241 	int	noerr;			/* FLAG, TRUE if all's well */
242 
243 	/* Get the dgrptab's pathname */
244 	if (dgrpname = _dgrptabpath()) {
245 
246 	    /* Unlink the existing file */
247 	    if (unlink(dgrpname) == 0) {
248 
249 		/* Make the temp file the real device-group table */
250 		noerr = (link(tempname, dgrpname) == 0) ? TRUE : FALSE;
251 
252 		/* Remove the temp file */
253 		if (noerr) noerr = rmnewdgrptab(tempname);
254 
255 	    } else noerr = FALSE;	/* unlink() failed */
256 
257 	    /* Free the dgrptab's name */
258 	    free(dgrpname);
259 
260 	} else noerr = FALSE; 	/* dgrptabpath() failed */
261 
262 	/* Finished.  Return success indicator */
263 	return (noerr);
264 }
265 
266 /*
267  * int lkdgrptab(o_mode, lktype)
268  *	char   *o_mode
269  *	short	lktype
270  *
271  *	Lock the device-group table for writing.  If it isn't available, it
272  *	waits until it is.
273  *
274  *  Arguments:
275  *	o_mode	The open() mode to use when opening the device-group table
276  *	lktype	The type of lock to apply
277  *
278  *  Returns:  int
279  *	TRUE if successful, FALSE with errno set otherwise
280  */
281 
282 static int
283 lkdgrptab(
284 	char   *o_mode,				/* Open mode */
285 	short	lktype)				/* Lock type */
286 {
287 	/* Automatic data */
288 	struct flock	lockinfo;		/* File locking structure */
289 	int		noerr;			/* FLAG, TRUE if no error */
290 	int		olderrno;		/* Former value of errno */
291 
292 
293 	/* Close the device-group table (if it's open) */
294 	_enddgrptab();
295 
296 	/* Open the device-group table for read/append */
297 	noerr = TRUE;
298 	if (_opendgrptab(o_mode)) {
299 
300 	/*
301 	 * Lock the device-group table (for writing).  If it's not
302 	 * available, wait until it is, then close and open the
303 	 * table (modify and delete change the table!) and try
304 	 * to lock it again
305 	 */
306 
307 	    /* Build the locking structure */
308 	    lockinfo.l_type = lktype;
309 	    lockinfo.l_whence = 0;
310 	    lockinfo.l_start = 0L;
311 	    lockinfo.l_len = 0L;
312 	    olderrno = errno;
313 
314 	    /* Keep on going until we lock the file or an error happens */
315 	    while ((fcntl(fileno(oam_dgroup), F_SETLK, &lockinfo) == -1) &&
316 		!noerr) {
317 
318 		/*
319 		 * fcntl() failed.
320 		 * If errno=EACCES, it's because the file's locked by someone
321 		 * else.  Wait for the file to be unlocked, then close and
322 		 * reopen the file and try the lock again.
323 		 */
324 
325 		if (errno == EACCES) {
326 		    if (fcntl(fileno(oam_dgroup), F_SETLKW, &lockinfo) == -1)
327 			noerr = FALSE;
328 		    else {
329 			_enddgrptab();
330 			if (!_opendgrptab(o_mode)) noerr = FALSE;
331 			else errno = olderrno;
332 		    }
333 
334 		} else noerr = FALSE;  /* fcntl() failed hard */
335 
336 	    }   /* End while (fcntl() && !noerr) */
337 
338 	    /* Don't keep file open if an error happened */
339 	    if (!noerr) _enddgrptab();
340 
341 	} else noerr = FALSE;	/* _opendgrptab() failed */
342 
343 	/* Done */
344 	return (noerr);
345 }
346 
347 /*
348  * int unlkdgrptab()
349  *
350  *	Unlock the locked device-group table.
351  *
352  *  Arguments:  None
353  *
354  *  Returns:  int
355  *	Whatever fcntl() returns...
356  */
357 
358 static int
359 unlkdgrptab(void)
360 {
361 	/* Automatic data */
362 	struct flock	lockinfo;		/* Locking structure */
363 	int		noerr;			/* FLAG, TRUE if all's well */
364 
365 	/* Build the locking structure */
366 	lockinfo.l_type = F_UNLCK;		/* Lock type */
367 	lockinfo.l_whence = 0;			/* Count from top of file */
368 	lockinfo.l_start = 0L;			/* From beginning */
369 	lockinfo.l_len = 0L;			/* Length of locked data */
370 
371 	/* Unlock it */
372 	noerr = (fcntl(fileno(oam_dgroup), F_SETLK, &lockinfo) != -1);
373 	_enddgrptab();
374 
375 	/* Finished */
376 	return (noerr);
377 }
378 
379 /*
380  * struct dgrptabent *mkdgrptabent(dgroup, members)
381  *	char   *dgroup
382  *	char  **members
383  *
384  *	This function builds a struct dgrptabent structure describing the
385  *	device-group <dgroup> so that it contains the members in the
386  *	membership list <members>.
387  *
388  *  Arguments:
389  *	dgroup		The device-group being added to the device-group table
390  *	members		The members of the device-group
391  *
392  *  Returns:  struct dgrptabent *
393  *	A completed struct dgrptabent structure containing the description
394  *	of the device group.  The structure, and all of the data in the
395  *	structure are each in space allocated using the malloc() function
396  *	and should be freed using the free() function (or the _freedgrptabent()
397  *	function.
398  */
399 
400 static struct dgrptabent *
401 mkdgrptabent(
402 	char   *dgroup,		/* Device-group being created (or modified) */
403 	char  **members)	/* Members to add to that entry */
404 {
405 	/* Automatic data */
406 	struct dgrptabent	*ent;	/* Ptr to struct we're making */
407 	struct member		*prev;	/* Ptr to prev attr/val struct */
408 	struct member		*member;	/* Ptr to current struct */
409 	char			**pp;	/* Ptr into list of ptrs */
410 	int			noerr;	/* TRUE if all's well */
411 
412 
413 	/* No problems (yet) */
414 	noerr = TRUE;
415 
416 	/* Get space for the structure */
417 	if (ent = malloc(sizeof (struct dgrptabent))) {
418 
419 	    /* Fill in default values */
420 	    ent->name = NULL; 				/* alias */
421 	    ent->entryno = 0;				/* Entry no. */
422 	    ent->comment = FALSE;			/* data rec */
423 	    ent->dataspace = NULL;			/* string */
424 	    ent->membership = NULL;			/* attr list */
425 
426 	    /* Fill in the device-group name */
427 	    if (ent->name = malloc(strlen(dgroup)+1)) {
428 		(void) strcpy(ent->name, dgroup);
429 
430 		/* Add membership to the structure */
431 		prev = NULL;
432 		if ((pp = members) != NULL)
433 		    while (*pp && noerr) {
434 
435 		    if (member = malloc(sizeof (struct member))) {
436 
437 			if (member->name = malloc(strlen(*pp)+1)) {
438 			    (void) strcpy(member->name, *pp);
439 			    if (prev) prev->next = member;
440 			    else ent->membership = member;
441 			    member->next = NULL;
442 			    prev = member;
443 			} else {
444 			    noerr = FALSE;
445 			    free(member);
446 			}
447 		    } else noerr = FALSE;
448 		    pp++;
449 		}   /* End membership processing loop */
450 
451 	    } else noerr = FALSE;	/* malloc() failed */
452 
453 		/*
454 		 * If there was a problem, clean up the mess we've made
455 		 */
456 
457 	    if (!noerr) {
458 
459 		_freedgrptabent(ent);
460 		ent = NULL;
461 
462 	    }   /* if (noerr) */
463 
464 	} else noerr = FALSE;   /* if (malloc(dgrptabent space)) */
465 
466 	/* Finished */
467 	return (ent);
468 }
469 
470 /*
471  * int _putdgrptabrec(stream, rec)
472  *	FILE		       *stream
473  *	struct dgrptabent      *rec
474  *
475  *	Write a device-group table record containing the information in the
476  *	struct dgrptab structure <rec> to the current position of the
477  *	standard I/O stream <stream>.
478  *
479  *  Arguments:
480  *	stream		The stream to write to
481  *	rec		The structure containing the information to write
482  *
483  *  Returns:  int
484  *	The number of characters written or EOF if there was some error.
485  */
486 
487 int
488 _putdgrptabrec(
489 	FILE			*stream,	/* Stream to write to */
490 	struct dgrptabent	*rec)		/* Record to write */
491 {
492 	/* Automatic Data */
493 	struct member		*mem;		/* Ptr to attr/val pair */
494 	char			*buf;		/* Allocated buffer */
495 	char			*p;		/* Temp char pointer */
496 	char			*q;		/* Temp char pointer */
497 	int			count;		/* Number of chars written */
498 	int			size;		/* Size of needed buffer */
499 
500 
501 	/* Comment or data record? */
502 	if (rec->comment) count = fputs(rec->dataspace, stream);
503 	else {
504 
505 	/*
506 	 * Record is a data record
507 	 */
508 
509 	    /* Figure out the amount of space the record needs */
510 	    size = (int)strlen(rec->name) + 1;	    /* "name:" */
511 	    if ((mem = rec->membership) != NULL)
512 		do {	    /* members */
513 		    /* "membername " or "membername\n" */
514 		    size += (int)strlen(mem->name) + 1;
515 		} while ((mem = mem->next) != NULL);	/* Next attr/val */
516 	    else
517 		size++;		/* Count trailing '\n' if empty grp */
518 
519 
520 	    /* Alloc space for the record */
521 	    if (buf = malloc((size_t) size+1)) {
522 
523 		/* Initializations */
524 		p = buf;
525 
526 		/* Write the device-group name */
527 		q = rec->name;
528 		while (*q) *p++ = *q++;
529 		*p++ = ':';
530 
531 		/* Write the membership list */
532 		if ((mem = rec->membership) != NULL) do {
533 		    q = mem->name;
534 		    while (*q) *p++ = *q++;
535 		    if ((mem = mem->next) != NULL) *p++ = ',';
536 		} while (mem);
537 
538 		/* Terminate the record */
539 		*p++ = '\n';
540 		*p = '\0';
541 
542 		/* Write the record */
543 		count = fputs(buf, stream);
544 		free(buf);
545 	    } else
546 		count = EOF;  /* malloc() failed */
547 	}
548 
549 	/* Finished */
550 	return (count);
551 }
552 
553 /*
554  *  int _adddgrptabrec(dgrp, members)
555  *	char   *dgrp
556  *	char  **members
557  *
558  *	If <dgrp> doesn't exist, this function adds a record to the
559  *	device-group table for that device-group.  That record will
560  *	have the name <dgrp> and will have a membership described in
561  *	the list referenced by <members>.  The record is added to the
562  *	end of the table.
563  *
564  *	If <dgrp> already exists in the table, the function adds the
565  *	members in the <members> list to the group's membership.
566  *
567  *  Arguments:
568  *	dgrp		The name of the device-group being added to the
569  *			device-group table.
570  *	members		A pointer to the first item of the list of members
571  *			in the device-group being added to the table.
572  *			(This value may be (char **) NULL).
573  *
574  *  Returns:  int
575  *	TRUE if successful, FALSE with "errno" set otherwise.
576  */
577 
578 int
579 _adddgrptabrec(
580 	char   *dgrp,			/* Devgrp to add to the table */
581 	char  **members)		/* Members for that devgrp */
582 {
583 	/* Automatic data */
584 	struct dgrptabent	*ent;		/* Ptr to dev tab entry */
585 	struct dgrptabent	*new;		/* Ptr to new dev tab info */
586 	struct dgrptabent	*p;		/* Temp ptr to dev tab info */
587 	struct member		*pm, *qm, *rm;	/* Tmp ptrs to struct member */
588 	FILE			*fd;		/* File descr, temp file */
589 	char			*path;		/* Ptr to new devtab name */
590 	int			olderrno;	/* Errno on entry */
591 	int			noerr;		/* FLAG, TRUE if all's well */
592 
593 
594 	/* Make a structure describing the new information */
595 	if ((new = mkdgrptabent(dgrp, members)) == NULL)
596 	    return (FALSE);
597 
598 	/*
599 	 * Lock the device-group table.  This only returns if the
600 	 * table is locked or some error occurred.  It waits until the
601 	 * table is available.
602 	 */
603 	if (!lkdgrptab("a+", F_WRLCK)) {
604 	    _freedgrptabent(new);
605 	    return (FALSE);
606 	}
607 
608 	/*
609 	 * If the device-group is already in the table, add
610 	 * the specified members
611 	 */
612 
613 	noerr = TRUE;
614 	olderrno = errno;
615 	if (ent = _getdgrprec(dgrp)) {
616 
617 	    /* Any members to add?  If not, do nothing. */
618 	    if (new->membership) {
619 
620 		/* Any existing members? */
621 		if ((pm = ent->membership) != NULL) {
622 
623 		    /* Find the end of the existing membership list */
624 		    while (pm->next) pm = pm->next;
625 
626 		    /* Append the new members to the membership list */
627 		    pm->next = new->membership;
628 
629 		    /* Remove any duplicates */
630 		    for (pm = ent->membership; pm; pm = pm->next) {
631 			qm = pm;
632 			while ((rm = qm->next) != NULL) {
633 			    if (strcmp(pm->name, rm->name) == 0) {
634 				qm->next = rm->next;
635 				free(rm->name);
636 				free(rm);
637 			    } else qm = rm;
638 			}
639 		    }
640 		} else ent->membership = new->membership;
641 
642 		/* No members in the new list any more */
643 		new->membership = NULL;
644 
645 		/*
646 		 * Make a new device-group table, replacing the
647 		 * record for the specified device-group
648 		 */
649 
650 		_setdgrptab();	/* Rewind existing table */
651 
652 		/* Open a temp file */
653 		if (fd = opennewdgrptab(&path)) {
654 
655 		    /* While there's more records and no error ... */
656 		    while (((p = _getdgrptabent()) != NULL) && noerr) {
657 
658 			/*
659 			 * If this isn't the record we're replacing,
660 			 * write it to the temporary file.  Otherwise,
661 			 * write the updated record
662 			 */
663 
664 			if (ent->entryno != p->entryno)
665 				noerr = _putdgrptabrec(fd, p) != EOF;
666 			else noerr = _putdgrptabrec(fd, ent) != EOF;
667 			_freedgrptabent(p);
668 		    }
669 
670 		    /* Fix the files */
671 		    if (noerr) {
672 			(void) fclose(fd);
673 			noerr = mknewdgrptab(path);
674 		    } else {
675 			(void) fclose(fd);
676 			(void) rmnewdgrptab(path);
677 		    }
678 		}   /* if (opennewdgrptab()) */
679 
680 	    }   /* If there's members to add */
681 
682 	    /* Free the memory associated with the updated entry */
683 	    _freedgrptabent(ent);
684 	}
685 
686 	/*
687 	 * Otherwise, add the device-group to the end of the table
688 	 */
689 
690 	else if (errno == EINVAL) {
691 	    errno = olderrno;
692 	    if (fseek(oam_dgroup, 0, SEEK_END) == 0)
693 		noerr = (_putdgrptabrec(oam_dgroup, new) != EOF);
694 	} else noerr = FALSE;
695 
696 	/* Finished */
697 	(void) unlkdgrptab();		/* Unlock the file */
698 	_freedgrptabent(new);		/* Free the new dgrptab info struct */
699 	return (noerr);			/* Return with success indicator */
700 }
701 
702 /*
703  * int _rmdgrptabrec(dgrp)
704  *	char   *dgrp
705  *
706  *	This function removes the record in the device-group table
707  *	for the specified device-group.
708  *
709  *  Arguments:
710  *	dgrp	The device-group to be removed
711  *
712  *  Returns:  int
713  *	Success indicator:  TRUE if successful, FALSE with errno set otherwise.
714  */
715 
716 int
717 _rmdgrptabrec(char *dgrp)		/* Device-group to remove */
718 {
719 	/* Automatic data */
720 	struct dgrptabent	*ent;	/* Entry to remove */
721 	struct dgrptabent	*p;	/* Entry being copied */
722 	FILE			*fd;	/* Temp file's file descriptor */
723 	char			*path;	/* Pathname of temp file */
724 	int			noerr;	/* FLAG, TRUE if all's well */
725 
726 	noerr = TRUE;
727 	if (!lkdgrptab("r", F_WRLCK))
728 		return (FALSE);
729 	if (ent = _getdgrprec(dgrp)) {
730 	    _setdgrptab();
731 	    if (fd = opennewdgrptab(&path)) {
732 		while (((p = _getdgrptabent()) != NULL) && noerr) {
733 		    if (ent->entryno != p->entryno)
734 			noerr = _putdgrptabrec(fd, p) != EOF;
735 		    _freedgrptabent(p);
736 		}
737 		if (noerr) {
738 		    (void) fclose(fd);
739 		    noerr = mknewdgrptab(path);
740 		} else {
741 		    (void) fclose(fd);
742 		    (void) rmnewdgrptab(path);
743 		}
744 	    } else noerr = FALSE;
745 	    _freedgrptabent(ent);
746 	} else noerr = FALSE;
747 	(void) unlkdgrptab();
748 	return (noerr);
749 }
750 
751 /*
752  * int _rmdgrpmems(dgrp, mems, notfounds)
753  *	char   *dgrp
754  *	char  **mems
755  *	char ***notfounds
756  *
757  *	Remove the specified members from the membership of the specified
758  *	device-group.  Any members not found in that device-group are
759  *	returned in the list referenced by <notfounds>.
760  *
761  *  Arguments:
762  *	dgrp		The device-group from which members are to be removed
763  *	mems		The address of the first element in the list of
764  *			members to remove.  This list is terminated by
765  *			(char *) NULL.
766  *	notfounds	The place to put the address of the list of addresses
767  *			referencing the requested members that were not
768  *			members of the specified device-group
769  *
770  *  Returns: int
771  *	TRUE if successful, FALSE with errno set otherwise.
772  */
773 
774 int
775 _rmdgrpmems(
776 	char   *dgrp,			/* Device-group to modify */
777 	char  **mems,			/* Members to remove */
778 	char ***notfounds)		/* Members req'd but not found */
779 {
780 	/* Automatic data */
781 	struct dgrptabent	*ent;	/* Entry to modify */
782 	struct dgrptabent	*p;	/* Entry being copied */
783 	struct member		*pm;	/* Ptr to member being examined */
784 	struct member		*prev;	/* Ptr to previous member */
785 	char			**nflst; /* Ptr to not-found list */
786 	char			**pnf;	/* Ptr into not-found list */
787 	char			**pp;	/* Ptr into members-to-rm list */
788 	FILE			*fd;	/* Temp file's file descriptor */
789 	char			*path;	/* Pathname of temp file */
790 	int			noerr;	/* TRUE if all's well */
791 	int			found;	/* TRUE if member is in membership */
792 	int			i;	/* Temp counter */
793 
794 	noerr = TRUE;
795 
796 	/* Lock the device-group table */
797 	if (!lkdgrptab("r", F_WRLCK))
798 		return (FALSE);
799 
800 	/* Nothing is "not found" yet */
801 	*notfounds = NULL;
802 
803 	/* Get the entry we're to modify */
804 	if (ent = _getdgrprec(dgrp)) {
805 
806 	    /* Allocate space for the not-found list */
807 	    i = 1;
808 	    if (mems)
809 		for (pp = mems; *pp; pp++)
810 			i++;
811 	    if (nflst = malloc(i*sizeof (char *))) {
812 		pnf = nflst;
813 		*pnf = NULL;
814 
815 		/* For each member to remove ... (if any) */
816 		if (mems)
817 		    for (pp = mems; *pp; pp++) {
818 
819 		    found = FALSE;
820 
821 		    /* Compare against each member in the membership list */
822 		    pm = ent->membership;
823 		    prev = NULL;
824 		    while (pm && !found) {
825 
826 			if (strcmp(*pp, pm->name) == 0) {
827 
828 			    /* Found.  Remove from linked list */
829 			    if (prev) prev->next = pm->next;
830 			    else ent->membership = pm->next;
831 			    if (pm->name) free(pm->name);
832 			    free(pm);
833 			    found = TRUE;
834 
835 			} else {
836 
837 			    /* Bump to the next member */
838 			    prev = pm;
839 			    pm = pm->next;
840 
841 			}
842 
843 		    }   /* For each member in the group */
844 
845 		/*
846 		 * If the requested member-to-remove wasn't found,
847 		 * add it to the list of not-found members
848 		 */
849 		    if (!found) {
850 			if (*pnf = malloc(strlen(*pp)+1)) {
851 			    (void) strcpy(*pnf++, *pp);
852 			    *pnf = NULL;
853 			} else noerr = FALSE;
854 		    }
855 
856 		}   /* for (each requested member to remove */
857 
858 		_setdgrptab();		/* Rewind existing table */
859 
860 		if (fd = opennewdgrptab(&path)) {
861 		    while (((p = _getdgrptabent()) != NULL) && noerr) {
862 			if (ent->entryno != p->entryno)
863 			    noerr = _putdgrptabrec(fd, p) != EOF;
864 			else noerr = _putdgrptabrec(fd, ent) != EOF;
865 			_freedgrptabent(p);
866 		    }
867 		    if (noerr) {
868 			(void) fclose(fd);
869 			noerr = mknewdgrptab(path);
870 		    } else {
871 			(void) fclose(fd);
872 			(void) rmnewdgrptab(path);
873 		    }
874 		} else noerr = FALSE;   /* if (opennewdgrptab()) */
875 
876 		/*
877 		 * If there was no error but there was requested members
878 		 * that weren't found, set the not-found list and the error
879 		 * information.  Otherwise, free the not-found list
880 		 */
881 
882 		if (noerr && (pnf != nflst)) {
883 		    *notfounds = nflst;
884 		    errno = ENODEV;
885 		    noerr = FALSE;
886 		} else {
887 		    for (pnf = nflst; *pnf; pnf++) free(*pnf);
888 		    free(nflst);
889 		    if (!noerr) *notfounds = NULL;
890 		}
891 	    } else noerr = FALSE;
892 
893 	    /* Free the description of the modified device group */
894 	    _freedgrptabent(ent);
895 
896 	} else noerr = FALSE;    /* _getdgrprec() failed */
897 
898 	/* Unlock the original device-group table */
899 	(void) unlkdgrptab();
900 	return (noerr);
901 }
902