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