gr_util.c revision 244777
1178431Sscf/*- 2178431Sscf * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org> 3178431Sscf * All rights reserved. 4178431Sscf * 5178431Sscf * Redistribution and use in source and binary forms, with or without 6178431Sscf * modification, are permitted provided that the following conditions 7178431Sscf * are met: 8178431Sscf * 1. Redistributions of source code must retain the above copyright 9178431Sscf * notice, this list of conditions and the following disclaimer, 10178431Sscf * without modification, immediately at the beginning of the file. 11178431Sscf * 2. Redistributions in binary form must reproduce the above copyright 12178431Sscf * notice, this list of conditions and the following disclaimer in the 13178431Sscf * documentation and/or other materials provided with the distribution. 14178431Sscf * 15178431Sscf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16178431Sscf * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17178431Sscf * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18178431Sscf * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19178431Sscf * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20178431Sscf * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21178431Sscf * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22178431Sscf * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23178431Sscf * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24178431Sscf * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25178431Sscf */ 26178431Sscf 27178431Sscf#include <sys/cdefs.h> 28178431Sscf__FBSDID("$FreeBSD: head/lib/libutil/gr_util.c 244777 2012-12-28 20:19:54Z bapt $"); 29178431Sscf 30178431Sscf#include <sys/param.h> 31228545Sbapt#include <sys/errno.h> 32228545Sbapt#include <sys/stat.h> 33184831Sscf 34228545Sbapt#include <ctype.h> 35228545Sbapt#include <err.h> 36228545Sbapt#include <fcntl.h> 37178431Sscf#include <grp.h> 38178431Sscf#include <inttypes.h> 39184831Sscf#include <libutil.h> 40228545Sbapt#include <paths.h> 41178431Sscf#include <stdbool.h> 42178431Sscf#include <stdio.h> 43178431Sscf#include <stdlib.h> 44178431Sscf#include <string.h> 45228545Sbapt#include <unistd.h> 46178431Sscf 47228545Sbaptstatic int lockfd = -1; 48228545Sbaptstatic char group_dir[PATH_MAX]; 49228545Sbaptstatic char group_file[PATH_MAX]; 50228545Sbaptstatic char tempname[PATH_MAX]; 51228545Sbaptstatic int initialized; 52228545Sbapt 53185237Sscfstatic const char group_line_format[] = "%s:%s:%ju:"; 54185237Sscf 55178431Sscf/* 56228545Sbapt * Initialize statics 57228545Sbapt */ 58228545Sbaptint 59228545Sbaptgr_init(const char *dir, const char *group) 60228545Sbapt{ 61242319Sbapt 62228545Sbapt if (dir == NULL) { 63228545Sbapt strcpy(group_dir, _PATH_ETC); 64228545Sbapt } else { 65228545Sbapt if (strlen(dir) >= sizeof(group_dir)) { 66228545Sbapt errno = ENAMETOOLONG; 67228545Sbapt return (-1); 68228545Sbapt } 69228545Sbapt strcpy(group_dir, dir); 70228545Sbapt } 71228545Sbapt 72228545Sbapt if (group == NULL) { 73228545Sbapt if (dir == NULL) { 74228545Sbapt strcpy(group_file, _PATH_GROUP); 75228545Sbapt } else if (snprintf(group_file, sizeof(group_file), "%s/group", 76228545Sbapt group_dir) > (int)sizeof(group_file)) { 77228545Sbapt errno = ENAMETOOLONG; 78228545Sbapt return (-1); 79228545Sbapt } 80228545Sbapt } else { 81228545Sbapt if (strlen(group) >= sizeof(group_file)) { 82228545Sbapt errno = ENAMETOOLONG; 83228545Sbapt return (-1); 84228545Sbapt } 85228545Sbapt strcpy(group_file, group); 86228545Sbapt } 87242319Sbapt 88228545Sbapt initialized = 1; 89228545Sbapt return (0); 90228545Sbapt} 91228545Sbapt 92228545Sbapt/* 93228545Sbapt * Lock the group file 94228545Sbapt */ 95228545Sbaptint 96228545Sbaptgr_lock(void) 97228545Sbapt{ 98228545Sbapt if (*group_file == '\0') 99228545Sbapt return (-1); 100228545Sbapt 101228545Sbapt for (;;) { 102228545Sbapt struct stat st; 103228545Sbapt 104244744Sbapt lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 105244735Sbapt if (lockfd == -1) { 106228545Sbapt if (errno == EWOULDBLOCK) { 107228545Sbapt errx(1, "the group file is busy"); 108228545Sbapt } else { 109228545Sbapt err(1, "could not lock the group file: "); 110228545Sbapt } 111228545Sbapt } 112228545Sbapt if (fstat(lockfd, &st) == -1) 113228545Sbapt err(1, "fstat() failed: "); 114228545Sbapt if (st.st_nlink != 0) 115228545Sbapt break; 116228545Sbapt close(lockfd); 117228545Sbapt lockfd = -1; 118228545Sbapt } 119228545Sbapt return (lockfd); 120228545Sbapt} 121228545Sbapt 122228545Sbapt/* 123228545Sbapt * Create and open a presmuably safe temp file for editing group data 124228545Sbapt */ 125228545Sbaptint 126228545Sbaptgr_tmp(int mfd) 127228545Sbapt{ 128228545Sbapt char buf[8192]; 129228545Sbapt ssize_t nr; 130228545Sbapt const char *p; 131228545Sbapt int tfd; 132228545Sbapt 133228545Sbapt if (*group_file == '\0') 134228545Sbapt return (-1); 135228545Sbapt if ((p = strrchr(group_file, '/'))) 136228545Sbapt ++p; 137228545Sbapt else 138228545Sbapt p = group_file; 139228545Sbapt if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", 140228545Sbapt (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { 141228545Sbapt errno = ENAMETOOLONG; 142228545Sbapt return (-1); 143228545Sbapt } 144228545Sbapt if ((tfd = mkstemp(tempname)) == -1) 145228545Sbapt return (-1); 146228545Sbapt if (mfd != -1) { 147228545Sbapt while ((nr = read(mfd, buf, sizeof(buf))) > 0) 148228545Sbapt if (write(tfd, buf, (size_t)nr) != nr) 149228545Sbapt break; 150228545Sbapt if (nr != 0) { 151228545Sbapt unlink(tempname); 152228545Sbapt *tempname = '\0'; 153228545Sbapt close(tfd); 154228545Sbapt return (-1); 155228545Sbapt } 156228545Sbapt } 157228545Sbapt return (tfd); 158228545Sbapt} 159228545Sbapt 160228545Sbapt/* 161228545Sbapt * Copy the group file from one descriptor to another, replacing, deleting 162228545Sbapt * or adding a single record on the way. 163228545Sbapt */ 164228545Sbaptint 165228545Sbaptgr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) 166228545Sbapt{ 167228545Sbapt char buf[8192], *end, *line, *p, *q, *r, t; 168228545Sbapt struct group *fgr; 169228545Sbapt const struct group *sgr; 170228545Sbapt size_t len; 171228545Sbapt int eof, readlen; 172228545Sbapt 173228545Sbapt sgr = gr; 174228545Sbapt if (gr == NULL) { 175228545Sbapt line = NULL; 176228545Sbapt if (old_gr == NULL) 177228545Sbapt return (-1); 178228545Sbapt sgr = old_gr; 179228545Sbapt } else if ((line = gr_make(gr)) == NULL) 180228545Sbapt return (-1); 181228545Sbapt 182228545Sbapt eof = 0; 183228545Sbapt len = 0; 184228545Sbapt p = q = end = buf; 185228545Sbapt for (;;) { 186228545Sbapt /* find the end of the current line */ 187228545Sbapt for (p = q; q < end && *q != '\0'; ++q) 188228545Sbapt if (*q == '\n') 189228545Sbapt break; 190228545Sbapt 191228545Sbapt /* if we don't have a complete line, fill up the buffer */ 192228545Sbapt if (q >= end) { 193228545Sbapt if (eof) 194228545Sbapt break; 195228545Sbapt if ((size_t)(q - p) >= sizeof(buf)) { 196228545Sbapt warnx("group line too long"); 197228545Sbapt errno = EINVAL; /* hack */ 198228545Sbapt goto err; 199228545Sbapt } 200228545Sbapt if (p < end) { 201228545Sbapt q = memmove(buf, p, end -p); 202228545Sbapt end -= p - buf; 203228545Sbapt } else { 204228545Sbapt p = q = end = buf; 205228545Sbapt } 206228545Sbapt readlen = read(ffd, end, sizeof(buf) - (end -buf)); 207228545Sbapt if (readlen == -1) 208228545Sbapt goto err; 209228545Sbapt else 210228545Sbapt len = (size_t)readlen; 211228545Sbapt if (len == 0 && p == buf) 212228545Sbapt break; 213228545Sbapt end += len; 214228545Sbapt len = end - buf; 215228545Sbapt if (len < (ssize_t)sizeof(buf)) { 216228545Sbapt eof = 1; 217228545Sbapt if (len > 0 && buf[len -1] != '\n') 218228545Sbapt ++len, *end++ = '\n'; 219228545Sbapt } 220228545Sbapt continue; 221228545Sbapt } 222228545Sbapt 223228545Sbapt /* is it a blank line or a comment? */ 224228545Sbapt for (r = p; r < q && isspace(*r); ++r) 225228545Sbapt /* nothing */; 226228545Sbapt if (r == q || *r == '#') { 227228545Sbapt /* yep */ 228228545Sbapt if (write(tfd, p, q -p + 1) != q - p + 1) 229228545Sbapt goto err; 230228545Sbapt ++q; 231228545Sbapt continue; 232228545Sbapt } 233228545Sbapt 234228545Sbapt /* is it the one we're looking for? */ 235228545Sbapt 236228545Sbapt t = *q; 237228545Sbapt *q = '\0'; 238228545Sbapt 239228545Sbapt fgr = gr_scan(r); 240228545Sbapt 241228545Sbapt /* fgr is either a struct group for the current line, 242228545Sbapt * or NULL if the line is malformed. 243228545Sbapt */ 244228545Sbapt 245228545Sbapt *q = t; 246228545Sbapt if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { 247228545Sbapt /* nope */ 248228545Sbapt if (fgr != NULL) 249228545Sbapt free(fgr); 250228545Sbapt if (write(tfd, p, q - p + 1) != q - p + 1) 251228545Sbapt goto err; 252228545Sbapt ++q; 253228545Sbapt continue; 254228545Sbapt } 255228545Sbapt if (old_gr && !gr_equal(fgr, old_gr)) { 256228545Sbapt warnx("entry inconsistent"); 257228545Sbapt free(fgr); 258228545Sbapt errno = EINVAL; /* hack */ 259228545Sbapt goto err; 260228545Sbapt } 261228545Sbapt free(fgr); 262228545Sbapt 263228545Sbapt /* it is, replace or remove it */ 264228545Sbapt if (line != NULL) { 265228545Sbapt len = strlen(line); 266228545Sbapt if (write(tfd, line, len) != (int) len) 267228545Sbapt goto err; 268228545Sbapt } else { 269228545Sbapt /* when removed, avoid the \n */ 270228545Sbapt q++; 271228545Sbapt } 272228545Sbapt /* we're done, just copy the rest over */ 273228545Sbapt for (;;) { 274228545Sbapt if (write(tfd, q, end - q) != end - q) 275228545Sbapt goto err; 276228545Sbapt q = buf; 277228545Sbapt readlen = read(ffd, buf, sizeof(buf)); 278228545Sbapt if (readlen == 0) 279228545Sbapt break; 280228545Sbapt else 281228545Sbapt len = (size_t)readlen; 282228545Sbapt if (readlen == -1) 283228545Sbapt goto err; 284228545Sbapt end = buf + len; 285228545Sbapt } 286228545Sbapt goto done; 287228545Sbapt } 288228545Sbapt 289228545Sbapt /* if we got here, we didn't find the old entry */ 290228545Sbapt if (line == NULL) { 291228545Sbapt errno = ENOENT; 292228545Sbapt goto err; 293228545Sbapt } 294228545Sbapt len = strlen(line); 295228545Sbapt if ((size_t)write(tfd, line, len) != len || 296228545Sbapt write(tfd, "\n", 1) != 1) 297228545Sbapt goto err; 298228545Sbapt done: 299228545Sbapt if (line != NULL) 300228545Sbapt free(line); 301228545Sbapt return (0); 302228545Sbapt err: 303228545Sbapt if (line != NULL) 304228545Sbapt free(line); 305228545Sbapt return (-1); 306228545Sbapt} 307228545Sbapt 308228545Sbapt/* 309228545Sbapt * Regenerate the group file 310228545Sbapt */ 311228545Sbaptint 312228545Sbaptgr_mkdb(void) 313228545Sbapt{ 314243334Sbapt if (chmod(tempname, 0644) != 0) 315243334Sbapt return (-1); 316243328Sbapt 317243334Sbapt return (rename(tempname, group_file)); 318228545Sbapt} 319228545Sbapt 320228545Sbapt/* 321228545Sbapt * Clean up. Preserver errno for the caller's convenience. 322228545Sbapt */ 323228545Sbaptvoid 324228545Sbaptgr_fini(void) 325228545Sbapt{ 326228545Sbapt int serrno; 327228545Sbapt 328228545Sbapt if (!initialized) 329228545Sbapt return; 330228545Sbapt initialized = 0; 331228545Sbapt serrno = errno; 332228545Sbapt if (*tempname != '\0') { 333228545Sbapt unlink(tempname); 334228545Sbapt *tempname = '\0'; 335228545Sbapt } 336228545Sbapt if (lockfd != -1) 337228545Sbapt close(lockfd); 338228545Sbapt errno = serrno; 339228545Sbapt} 340228545Sbapt 341228545Sbapt/* 342178431Sscf * Compares two struct group's. 343178431Sscf */ 344178431Sscfint 345178431Sscfgr_equal(const struct group *gr1, const struct group *gr2) 346178431Sscf{ 347185237Sscf int gr1_ndx; 348185237Sscf int gr2_ndx; 349184831Sscf bool found; 350178431Sscf 351178431Sscf /* Check that the non-member information is the same. */ 352185237Sscf if (gr1->gr_name == NULL || gr2->gr_name == NULL) { 353185237Sscf if (gr1->gr_name != gr2->gr_name) 354185237Sscf return (false); 355185237Sscf } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) 356185237Sscf return (false); 357185237Sscf if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { 358185237Sscf if (gr1->gr_passwd != gr2->gr_passwd) 359185237Sscf return (false); 360185237Sscf } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) 361185237Sscf return (false); 362185237Sscf if (gr1->gr_gid != gr2->gr_gid) 363185237Sscf return (false); 364178431Sscf 365178431Sscf /* Check all members in both groups. */ 366185237Sscf if (gr1->gr_mem == NULL || gr2->gr_mem == NULL) { 367185237Sscf if (gr1->gr_mem != gr2->gr_mem) 368185237Sscf return (false); 369185237Sscf } else { 370185237Sscf for (found = false, gr1_ndx = 0; gr1->gr_mem[gr1_ndx] != NULL; 371185237Sscf gr1_ndx++) { 372185237Sscf for (gr2_ndx = 0; gr2->gr_mem[gr2_ndx] != NULL; 373185237Sscf gr2_ndx++) 374185237Sscf if (strcmp(gr1->gr_mem[gr1_ndx], 375185237Sscf gr2->gr_mem[gr2_ndx]) == 0) { 376178431Sscf found = true; 377178431Sscf break; 378178431Sscf } 379185237Sscf if (!found) 380185237Sscf return (false); 381178431Sscf } 382178431Sscf 383178431Sscf /* Check that group2 does not have more members than group1. */ 384185237Sscf if (gr2->gr_mem[gr1_ndx] != NULL) 385185237Sscf return (false); 386178431Sscf } 387178431Sscf 388185237Sscf return (true); 389178431Sscf} 390178431Sscf 391178431Sscf/* 392178431Sscf * Make a group line out of a struct group. 393178431Sscf */ 394178431Sscfchar * 395178431Sscfgr_make(const struct group *gr) 396178431Sscf{ 397178431Sscf char *line; 398185237Sscf size_t line_size; 399178431Sscf int ndx; 400178431Sscf 401178431Sscf /* Calculate the length of the group line. */ 402185237Sscf line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, 403178431Sscf gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; 404185237Sscf if (gr->gr_mem != NULL) { 405185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) 406185237Sscf line_size += strlen(gr->gr_mem[ndx]) + 1; 407185237Sscf if (ndx > 0) 408185237Sscf line_size--; 409185237Sscf } 410178431Sscf 411178431Sscf /* Create the group line and fill it. */ 412185237Sscf if ((line = malloc(line_size)) == NULL) 413178431Sscf return (NULL); 414200423Sscf snprintf(line, line_size, group_line_format, gr->gr_name, gr->gr_passwd, 415200423Sscf (uintmax_t)gr->gr_gid); 416185237Sscf if (gr->gr_mem != NULL) 417185237Sscf for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { 418185237Sscf strcat(line, gr->gr_mem[ndx]); 419185237Sscf if (gr->gr_mem[ndx + 1] != NULL) 420185237Sscf strcat(line, ","); 421185237Sscf } 422178431Sscf 423178431Sscf return (line); 424178431Sscf} 425178431Sscf 426178431Sscf/* 427178431Sscf * Duplicate a struct group. 428178431Sscf */ 429178431Sscfstruct group * 430178431Sscfgr_dup(const struct group *gr) 431178431Sscf{ 432244742Sbapt struct group *newgr; 433185237Sscf char *dst; 434184831Sscf size_t len; 435178431Sscf int ndx; 436185237Sscf int num_mem; 437178431Sscf 438185237Sscf /* Calculate size of the group. */ 439244742Sbapt len = sizeof(*newgr); 440185237Sscf if (gr->gr_name != NULL) 441185237Sscf len += strlen(gr->gr_name) + 1; 442185237Sscf if (gr->gr_passwd != NULL) 443185237Sscf len += strlen(gr->gr_passwd) + 1; 444178431Sscf if (gr->gr_mem != NULL) { 445185237Sscf for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) 446185237Sscf len += strlen(gr->gr_mem[num_mem]) + 1; 447185237Sscf len += (num_mem + 1) * sizeof(*gr->gr_mem); 448185237Sscf } else 449185237Sscf num_mem = -1; 450178431Sscf /* Create new group and copy old group into it. */ 451244742Sbapt if ((newgr = malloc(len)) == NULL) 452178431Sscf return (NULL); 453244742Sbapt /* point new gr_mem to end of struct + 1 */ 454244742Sbapt if (gr->gr_mem != NULL) 455244747Sbapt newgr->gr_mem = (char **)(newgr + 1); 456244742Sbapt else 457244742Sbapt newgr->gr_mem = NULL; 458244742Sbapt /* point dst after the end of all the gr_mem pointers in newgr */ 459244742Sbapt dst = (char *)newgr + sizeof(struct group) + 460244742Sbapt (num_mem + 1) * sizeof(*gr->gr_mem); 461178431Sscf if (gr->gr_name != NULL) { 462244742Sbapt newgr->gr_name = dst; 463244742Sbapt dst = stpcpy(dst, gr->gr_name) + 1; 464244777Sbapt } else { 465244777Sbapt newgr->gr_name = NULL; 466178431Sscf } 467178431Sscf if (gr->gr_passwd != NULL) { 468244742Sbapt newgr->gr_passwd = dst; 469244742Sbapt dst = stpcpy(dst, gr->gr_passwd) + 1; 470244777Sbapt } else { 471244777Sbapt newgr->gr_passwd = NULL; 472178431Sscf } 473244742Sbapt newgr->gr_gid = gr->gr_gid; 474178431Sscf if (gr->gr_mem != NULL) { 475185237Sscf for (ndx = 0; ndx < num_mem; ndx++) { 476244742Sbapt newgr->gr_mem[ndx] = dst; 477244742Sbapt dst = stpcpy(dst, gr->gr_mem[ndx]) + 1; 478178431Sscf } 479244742Sbapt newgr->gr_mem[ndx] = NULL; 480178431Sscf } 481244742Sbapt return (newgr); 482178431Sscf} 483178431Sscf 484178431Sscf/* 485244736Sbapt * Add a new member name to a struct group. 486244736Sbapt */ 487244736Sbaptstruct group * 488244739Sbaptgr_add(struct group *gr, char *newmember) 489244736Sbapt{ 490244736Sbapt size_t mlen; 491244736Sbapt int num_mem=0; 492244736Sbapt char **members; 493244736Sbapt struct group *newgr; 494244736Sbapt 495244736Sbapt if (newmember == NULL) 496244736Sbapt return(gr_dup(gr)); 497244736Sbapt 498244736Sbapt if (gr->gr_mem != NULL) { 499244736Sbapt for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) { 500244736Sbapt if (strcmp(gr->gr_mem[num_mem], newmember) == 0) { 501244736Sbapt errno = EEXIST; 502244736Sbapt return (NULL); 503244736Sbapt } 504244736Sbapt } 505244736Sbapt } 506244736Sbapt /* Allocate enough for current pointers + 1 more and NULL marker */ 507244736Sbapt mlen = (num_mem + 2) * sizeof(*gr->gr_mem); 508244739Sbapt if ((members = malloc(mlen)) == NULL) { 509244736Sbapt errno = ENOMEM; 510244736Sbapt return (NULL); 511244736Sbapt } 512244736Sbapt memcpy(members, gr->gr_mem, num_mem * sizeof(*gr->gr_mem)); 513244739Sbapt members[num_mem++] = newmember; 514244736Sbapt members[num_mem] = NULL; 515244736Sbapt gr->gr_mem = members; 516244736Sbapt newgr = gr_dup(gr); 517244736Sbapt if (newgr == NULL) 518244736Sbapt errno = ENOMEM; 519244736Sbapt free(members); 520244736Sbapt return (newgr); 521244736Sbapt} 522244736Sbapt 523244736Sbapt/* 524178431Sscf * Scan a line and place it into a group structure. 525178431Sscf */ 526178431Sscfstatic bool 527178431Sscf__gr_scan(char *line, struct group *gr) 528178431Sscf{ 529178431Sscf char *loc; 530178431Sscf int ndx; 531178431Sscf 532178431Sscf /* Assign non-member information to structure. */ 533178431Sscf gr->gr_name = line; 534178431Sscf if ((loc = strchr(line, ':')) == NULL) 535178431Sscf return (false); 536178431Sscf *loc = '\0'; 537178431Sscf gr->gr_passwd = loc + 1; 538184831Sscf if (*gr->gr_passwd == ':') 539184831Sscf *gr->gr_passwd = '\0'; 540178431Sscf else { 541178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 542178431Sscf return (false); 543178431Sscf *loc = '\0'; 544178431Sscf } 545184831Sscf if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) 546178431Sscf return (false); 547178431Sscf 548178431Sscf /* Assign member information to structure. */ 549178431Sscf if ((loc = strchr(loc + 1, ':')) == NULL) 550178431Sscf return (false); 551178431Sscf line = loc + 1; 552178431Sscf gr->gr_mem = NULL; 553185237Sscf ndx = 0; 554185237Sscf do { 555185237Sscf gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * 556185237Sscf (ndx + 1)); 557185237Sscf if (gr->gr_mem == NULL) 558185237Sscf return (false); 559185237Sscf 560185237Sscf /* Skip locations without members (i.e., empty string). */ 561178431Sscf do { 562178431Sscf gr->gr_mem[ndx] = strsep(&line, ","); 563185237Sscf } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); 564185237Sscf } while (gr->gr_mem[ndx++] != NULL); 565178431Sscf 566178431Sscf return (true); 567178431Sscf} 568178431Sscf 569178431Sscf/* 570178431Sscf * Create a struct group from a line. 571178431Sscf */ 572178431Sscfstruct group * 573178431Sscfgr_scan(const char *line) 574178431Sscf{ 575184831Sscf struct group gr; 576185237Sscf char *line_copy; 577185237Sscf struct group *new_gr; 578178431Sscf 579185237Sscf if ((line_copy = strdup(line)) == NULL) 580178431Sscf return (NULL); 581185237Sscf if (!__gr_scan(line_copy, &gr)) { 582185237Sscf free(line_copy); 583178431Sscf return (NULL); 584178431Sscf } 585185237Sscf new_gr = gr_dup(&gr); 586185237Sscf free(line_copy); 587178431Sscf if (gr.gr_mem != NULL) 588178431Sscf free(gr.gr_mem); 589178431Sscf 590185237Sscf return (new_gr); 591178431Sscf} 592