pw_conf.c revision 81977
1256056Sgrehan/*- 2256056Sgrehan * Copyright (C) 1996 3256056Sgrehan * David L. Nugent. All rights reserved. 4256056Sgrehan * 5256056Sgrehan * Redistribution and use in source and binary forms, with or without 6256056Sgrehan * modification, are permitted provided that the following conditions 7256056Sgrehan * are met: 8256056Sgrehan * 1. Redistributions of source code must retain the above copyright 9256056Sgrehan * notice, this list of conditions and the following disclaimer. 10256056Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11256056Sgrehan * notice, this list of conditions and the following disclaimer in the 12256056Sgrehan * documentation and/or other materials provided with the distribution. 13256056Sgrehan * 14256056Sgrehan * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15256056Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16256056Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17256056Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18256056Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19256056Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20256056Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21256056Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22256056Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23256056Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24256056Sgrehan * SUCH DAMAGE. 25256056Sgrehan */ 26256056Sgrehan 27256056Sgrehan#ifndef lint 28256056Sgrehanstatic const char rcsid[] = 29256056Sgrehan "$FreeBSD: head/usr.sbin/pw/pw_conf.c 81977 2001-08-20 13:24:39Z brian $"; 30256056Sgrehan#endif /* not lint */ 31256056Sgrehan 32256056Sgrehan#include <string.h> 33256056Sgrehan#include <ctype.h> 34256056Sgrehan#include <fcntl.h> 35256056Sgrehan 36256056Sgrehan#include "pw.h" 37256056Sgrehan 38256056Sgrehan#define debugging 0 39256056Sgrehan 40256056Sgrehanenum { 41256056Sgrehan _UC_NONE, 42256056Sgrehan _UC_DEFAULTPWD, 43256056Sgrehan _UC_REUSEUID, 44256056Sgrehan _UC_REUSEGID, 45256056Sgrehan _UC_NISPASSWD, 46256056Sgrehan _UC_DOTDIR, 47256056Sgrehan _UC_NEWMAIL, 48256056Sgrehan _UC_LOGFILE, 49256056Sgrehan _UC_HOMEROOT, 50256056Sgrehan _UC_SHELLPATH, 51276349Sneel _UC_SHELLS, 52256056Sgrehan _UC_DEFAULTSHELL, 53280745Smav _UC_DEFAULTGROUP, 54256056Sgrehan _UC_EXTRAGROUPS, 55256056Sgrehan _UC_DEFAULTCLASS, 56256056Sgrehan _UC_MINUID, 57256056Sgrehan _UC_MAXUID, 58256056Sgrehan _UC_MINGID, 59256056Sgrehan _UC_MAXGID, 60256056Sgrehan _UC_EXPIRE, 61256056Sgrehan _UC_PASSWORD, 62256056Sgrehan _UC_FIELDS 63256056Sgrehan}; 64256056Sgrehan 65256056Sgrehanstatic char bourne_shell[] = "sh"; 66256056Sgrehan 67256056Sgrehanstatic char *system_shells[_UC_MAXSHELLS] = 68256056Sgrehan{ 69256056Sgrehan bourne_shell, 70256056Sgrehan "csh", 71256056Sgrehan "tcsh" 72256056Sgrehan}; 73256056Sgrehan 74256056Sgrehanstatic char const *booltrue[] = 75256056Sgrehan{ 76256056Sgrehan "yes", "true", "1", "on", NULL 77256056Sgrehan}; 78256056Sgrehanstatic char const *boolfalse[] = 79256056Sgrehan{ 80256056Sgrehan "no", "false", "0", "off", NULL 81256056Sgrehan}; 82256056Sgrehan 83256056Sgrehanstatic struct userconf config = 84256056Sgrehan{ 85256056Sgrehan 0, /* Default password for new users? (nologin) */ 86256056Sgrehan 0, /* Reuse uids? */ 87256056Sgrehan 0, /* Reuse gids? */ 88256056Sgrehan NULL, /* NIS version of the passwd file */ 89256056Sgrehan "/usr/share/skel", /* Where to obtain skeleton files */ 90280740Smav NULL, /* Mail to send to new accounts */ 91256056Sgrehan "/var/log/userlog", /* Where to log changes */ 92256056Sgrehan "/home", /* Where to create home directory */ 93256056Sgrehan "/bin", /* Where shells are located */ 94256056Sgrehan system_shells, /* List of shells (first is default) */ 95256056Sgrehan bourne_shell, /* Default shell */ 96256056Sgrehan NULL, /* Default group name */ 97256056Sgrehan NULL, /* Default (additional) groups */ 98256056Sgrehan NULL, /* Default login class */ 99256056Sgrehan 1000, 32000, /* Allowed range of uids */ 100256056Sgrehan 1000, 32000, /* Allowed range of gids */ 101267339Sjhb 0, /* Days until account expires */ 102267339Sjhb 0, /* Days until password expires */ 103267339Sjhb 0 /* size of default_group array */ 104267339Sjhb}; 105267339Sjhb 106267339Sjhbstatic char const *comments[_UC_FIELDS] = 107267339Sjhb{ 108256056Sgrehan "#\n# pw.conf - user/group configuration defaults\n#\n", 109256056Sgrehan "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 110256056Sgrehan "\n# Reuse gaps in uid sequence? (yes or no)\n", 111256056Sgrehan "\n# Reuse gaps in gid sequence? (yes or no)\n", 112256056Sgrehan "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 113256056Sgrehan "\n# Obtain default dotfiles from this directory\n", 114256056Sgrehan "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 115256056Sgrehan "\n# Log add/change/remove information in this file\n", 116256056Sgrehan "\n# Root directory in which $HOME directory is created\n", 117256056Sgrehan "\n# Colon separated list of directories containing valid shells\n", 118256056Sgrehan "\n# Comma separated list of available shells (without paths)\n", 119256056Sgrehan "\n# Default shell (without path)\n", 120256056Sgrehan "\n# Default group (leave blank for new group per user)\n", 121276349Sneel "\n# Extra groups for new users\n", 122276349Sneel "\n# Default login class for new users\n", 123256056Sgrehan "\n# Range of valid default user ids\n", 124256056Sgrehan NULL, 125256056Sgrehan "\n# Range of valid default group ids\n", 126256056Sgrehan NULL, 127282306Smav "\n# Days after which account expires (0=disabled)\n", 128256056Sgrehan "\n# Days after which password expires (0=disabled)\n" 129256056Sgrehan}; 130256056Sgrehan 131256056Sgrehanstatic char const *kwds[] = 132256056Sgrehan{ 133256164Sdim "", 134256164Sdim "defaultpasswd", 135280745Smav "reuseuids", 136256056Sgrehan "reusegids", 137256056Sgrehan "nispasswd", 138282846Smav "skeleton", 139256056Sgrehan "newmail", 140256056Sgrehan "logfile", 141280736Smav "home", 142256056Sgrehan "shellpath", 143256056Sgrehan "shells", 144282846Smav "defaultshell", 145267339Sjhb "defaultgroup", 146256056Sgrehan "extragroups", 147256056Sgrehan "defaultclass", 148256056Sgrehan "minuid", 149256056Sgrehan "maxuid", 150256056Sgrehan "mingid", 151256056Sgrehan "maxgid", 152256056Sgrehan "expire_days", 153256056Sgrehan "password_days", 154256056Sgrehan NULL 155256056Sgrehan}; 156256056Sgrehan 157256056Sgrehanstatic char * 158256056Sgrehanunquote(char const * str) 159256056Sgrehan{ 160256056Sgrehan if (str && (*str == '"' || *str == '\'')) { 161256056Sgrehan char *p = strchr(str + 1, *str); 162256056Sgrehan 163256056Sgrehan if (p != NULL) 164256056Sgrehan *p = '\0'; 165256056Sgrehan return (char *) (*++str ? str : NULL); 166256056Sgrehan } 167256056Sgrehan return (char *) str; 168256056Sgrehan} 169256056Sgrehan 170256056Sgrehanint 171276349Sneelboolean_val(char const * str, int dflt) 172256056Sgrehan{ 173256056Sgrehan if ((str = unquote(str)) != NULL) { 174256056Sgrehan int i; 175256056Sgrehan 176256056Sgrehan for (i = 0; booltrue[i]; i++) 177256056Sgrehan if (strcmp(str, booltrue[i]) == 0) 178256056Sgrehan return 1; 179256056Sgrehan for (i = 0; boolfalse[i]; i++) 180256056Sgrehan if (strcmp(str, boolfalse[i]) == 0) 181256056Sgrehan return 0; 182256056Sgrehan 183256056Sgrehan /* 184256056Sgrehan * Special cases for defaultpassword 185259301Sgrehan */ 186256056Sgrehan if (strcmp(str, "random") == 0) 187256056Sgrehan return -1; 188256056Sgrehan if (strcmp(str, "none") == 0) 189256056Sgrehan return -2; 190256056Sgrehan } 191256056Sgrehan return dflt; 192256056Sgrehan} 193256056Sgrehan 194256056Sgrehanchar const * 195256056Sgrehanboolean_str(int val) 196256056Sgrehan{ 197256056Sgrehan if (val == -1) 198256056Sgrehan return "random"; 199256056Sgrehan else if (val == -2) 200256056Sgrehan return "none"; 201256056Sgrehan else 202256056Sgrehan return val ? booltrue[0] : boolfalse[0]; 203256056Sgrehan} 204267393Sjhb 205256056Sgrehanchar * 206256056Sgrehannewstr(char const * p) 207256056Sgrehan{ 208256056Sgrehan char *q = NULL; 209282846Smav 210282846Smav if ((p = unquote(p)) != NULL) { 211256056Sgrehan int l = strlen(p) + 1; 212256056Sgrehan 213256056Sgrehan if ((q = malloc(l)) != NULL) 214256056Sgrehan memcpy(q, p, l); 215256056Sgrehan } 216256056Sgrehan return q; 217256056Sgrehan} 218256056Sgrehan 219256056Sgrehan#define LNBUFSZ 1024 220256056Sgrehan 221256056Sgrehan 222256056Sgrehanstruct userconf * 223256056Sgrehanread_userconfig(char const * file) 224256056Sgrehan{ 225256056Sgrehan FILE *fp; 226267393Sjhb 227256056Sgrehan extendarray(&config.groups, &config.numgroups, 200); 228256056Sgrehan memset(config.groups, 0, config.numgroups * sizeof(char *)); 229267393Sjhb if (file == NULL) 230267393Sjhb file = _PATH_PW_CONF; 231256056Sgrehan if ((fp = fopen(file, "r")) != NULL) { 232256056Sgrehan int buflen = LNBUFSZ; 233256056Sgrehan char *buf = malloc(buflen); 234256056Sgrehan 235256056Sgrehan nextline: 236256056Sgrehan while (fgets(buf, buflen, fp) != NULL) { 237256056Sgrehan char *p; 238256056Sgrehan 239256056Sgrehan while ((p = strchr(buf, '\n')) == NULL) { 240267393Sjhb int l; 241267393Sjhb if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) { 242267393Sjhb int ch; 243267393Sjhb while ((ch = fgetc(fp)) != '\n' && ch != EOF); 244267393Sjhb goto nextline; /* Ignore it */ 245267393Sjhb } 246267393Sjhb l = strlen(buf); 247267393Sjhb if (fgets(buf + l, buflen - l, fp) == NULL) 248267393Sjhb break; /* Unterminated last line */ 249267393Sjhb } 250267393Sjhb 251267393Sjhb if (p != NULL) 252267393Sjhb *p = '\0'; 253267393Sjhb 254267393Sjhb if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 255267393Sjhb static char const toks[] = " \t\r\n,="; 256267393Sjhb char *q = strtok(NULL, toks); 257267393Sjhb int i = 0; 258267393Sjhb 259267393Sjhb while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 260267393Sjhb ++i; 261267393Sjhb#if debugging 262256056Sgrehan if (i == _UC_FIELDS) 263256056Sgrehan printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 264256056Sgrehan else 265256056Sgrehan printf("Got kwd[%s]=%s\n", p, q); 266256056Sgrehan#endif 267256056Sgrehan switch (i) { 268256056Sgrehan case _UC_DEFAULTPWD: 269256164Sdim config.default_password = boolean_val(q, 1); 270256056Sgrehan break; 271256056Sgrehan case _UC_REUSEUID: 272256056Sgrehan config.reuse_uids = boolean_val(q, 0); 273256056Sgrehan break; 274256056Sgrehan case _UC_REUSEGID: 275256056Sgrehan config.reuse_gids = boolean_val(q, 0); 276282846Smav break; 277256056Sgrehan case _UC_NISPASSWD: 278256056Sgrehan config.nispasswd = (q == NULL || !boolean_val(q, 1)) 279256056Sgrehan ? NULL : newstr(q); 280256056Sgrehan break; 281282846Smav case _UC_DOTDIR: 282256056Sgrehan config.dotdir = (q == NULL || !boolean_val(q, 1)) 283256056Sgrehan ? NULL : newstr(q); 284256056Sgrehan break; 285256056Sgrehan case _UC_NEWMAIL: 286282846Smav config.newmail = (q == NULL || !boolean_val(q, 1)) 287256056Sgrehan ? NULL : newstr(q); 288256056Sgrehan break; 289256056Sgrehan case _UC_LOGFILE: 290256056Sgrehan config.logfile = (q == NULL || !boolean_val(q, 1)) 291256056Sgrehan ? NULL : newstr(q); 292282846Smav break; 293282846Smav case _UC_HOMEROOT: 294282846Smav config.home = (q == NULL || !boolean_val(q, 1)) 295282846Smav ? "/home" : newstr(q); 296256056Sgrehan break; 297256056Sgrehan case _UC_SHELLPATH: 298256056Sgrehan config.shelldir = (q == NULL || !boolean_val(q, 1)) 299256056Sgrehan ? "/bin" : newstr(q); 300256056Sgrehan break; 301256056Sgrehan case _UC_SHELLS: 302256056Sgrehan for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 303256056Sgrehan system_shells[i] = newstr(q); 304267339Sjhb if (i > 0) 305267339Sjhb while (i < _UC_MAXSHELLS) 306267339Sjhb system_shells[i++] = NULL; 307267339Sjhb break; 308267339Sjhb case _UC_DEFAULTSHELL: 309267339Sjhb config.shell_default = (q == NULL || !boolean_val(q, 1)) 310267339Sjhb ? (char *) bourne_shell : newstr(q); 311267339Sjhb break; 312267339Sjhb case _UC_DEFAULTGROUP: 313267339Sjhb q = unquote(q); 314280736Smav config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 315256056Sgrehan ? NULL : newstr(q); 316256056Sgrehan break; 317256056Sgrehan case _UC_EXTRAGROUPS: 318256056Sgrehan for (i = 0; q != NULL; q = strtok(NULL, toks)) { 319256056Sgrehan if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 320282846Smav config.groups[i++] = newstr(q); 321256056Sgrehan } 322280736Smav if (i > 0) 323280736Smav while (i < config.numgroups) 324282846Smav config.groups[i++] = NULL; 325280736Smav break; 326280736Smav case _UC_DEFAULTCLASS: 327280736Smav config.default_class = (q == NULL || !boolean_val(q, 1)) 328282846Smav ? NULL : newstr(q); 329280736Smav break; 330280736Smav case _UC_MINUID: 331280736Smav if ((q = unquote(q)) != NULL && isdigit(*q)) 332280736Smav config.min_uid = (uid_t) atol(q); 333280736Smav break; 334280736Smav case _UC_MAXUID: 335282846Smav if ((q = unquote(q)) != NULL && isdigit(*q)) 336282846Smav config.max_uid = (uid_t) atol(q); 337256056Sgrehan break; 338256056Sgrehan case _UC_MINGID: 339256056Sgrehan if ((q = unquote(q)) != NULL && isdigit(*q)) 340256056Sgrehan config.min_gid = (gid_t) atol(q); 341256056Sgrehan break; 342256056Sgrehan case _UC_MAXGID: 343256056Sgrehan if ((q = unquote(q)) != NULL && isdigit(*q)) 344256056Sgrehan config.max_gid = (gid_t) atol(q); 345256056Sgrehan break; 346256056Sgrehan case _UC_EXPIRE: 347256056Sgrehan if ((q = unquote(q)) != NULL && isdigit(*q)) 348256056Sgrehan config.expire_days = atoi(q); 349256056Sgrehan break; 350256056Sgrehan case _UC_PASSWORD: 351256056Sgrehan if ((q = unquote(q)) != NULL && isdigit(*q)) 352256056Sgrehan config.password_days = atoi(q); 353256056Sgrehan break; 354256056Sgrehan case _UC_FIELDS: 355256056Sgrehan case _UC_NONE: 356256056Sgrehan break; 357256056Sgrehan } 358256056Sgrehan } 359256056Sgrehan } 360256056Sgrehan free(buf); 361256056Sgrehan fclose(fp); 362280736Smav } 363280736Smav return &config; 364280736Smav} 365280736Smav 366280736Smav 367280736Smavint 368270159Sgrehanwrite_userconfig(char const * file) 369256056Sgrehan{ 370256056Sgrehan int fd; 371256056Sgrehan 372256056Sgrehan if (file == NULL) 373256056Sgrehan file = _PATH_PW_CONF; 374282846Smav 375282846Smav if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 376282846Smav FILE *fp; 377282846Smav 378282846Smav if ((fp = fdopen(fd, "w")) == NULL) 379282846Smav close(fd); 380282846Smav else { 381282846Smav int i, j, k; 382282846Smav int len = LNBUFSZ; 383282846Smav char *buf = malloc(len); 384282846Smav 385282846Smav for (i = _UC_NONE; i < _UC_FIELDS; i++) { 386282846Smav int quote = 1; 387282846Smav char const *val = buf; 388282846Smav 389256056Sgrehan *buf = '\0'; 390256056Sgrehan switch (i) { 391256056Sgrehan case _UC_DEFAULTPWD: 392256056Sgrehan val = boolean_str(config.default_password); 393256056Sgrehan break; 394256056Sgrehan case _UC_REUSEUID: 395256056Sgrehan val = boolean_str(config.reuse_uids); 396256056Sgrehan break; 397256056Sgrehan case _UC_REUSEGID: 398256056Sgrehan val = boolean_str(config.reuse_gids); 399256056Sgrehan break; 400256056Sgrehan case _UC_NISPASSWD: 401256056Sgrehan val = config.nispasswd ? config.nispasswd : ""; 402256056Sgrehan quote = 0; 403256056Sgrehan break; 404256056Sgrehan case _UC_DOTDIR: 405256056Sgrehan val = config.dotdir ? config.dotdir : boolean_str(0); 406276349Sneel break; 407276349Sneel case _UC_NEWMAIL: 408276349Sneel val = config.newmail ? config.newmail : boolean_str(0); 409276349Sneel break; 410276429Sneel case _UC_LOGFILE: 411276429Sneel val = config.logfile ? config.logfile : boolean_str(0); 412276349Sneel break; 413276349Sneel case _UC_HOMEROOT: 414276429Sneel val = config.home; 415282846Smav break; 416276349Sneel case _UC_SHELLPATH: 417276429Sneel val = config.shelldir; 418276429Sneel break; 419282846Smav case _UC_SHELLS: 420276429Sneel for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) { 421276349Sneel char lbuf[64]; 422276349Sneel int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]); 423276349Sneel if (l == -1) 424276349Sneel l = 0; 425276349Sneel if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) { 426276349Sneel strcpy(buf + k, lbuf); 427276349Sneel k += l; 428276349Sneel } 429276349Sneel } 430276349Sneel quote = 0; 431276349Sneel break; 432276349Sneel case _UC_DEFAULTSHELL: 433276349Sneel val = config.shell_default ? config.shell_default : bourne_shell; 434276349Sneel break; 435276349Sneel case _UC_DEFAULTGROUP: 436276349Sneel val = config.default_group ? config.default_group : ""; 437276349Sneel break; 438276349Sneel case _UC_EXTRAGROUPS: 439276349Sneel extendarray(&config.groups, &config.numgroups, 200); 440276349Sneel for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) { 441276349Sneel char lbuf[64]; 442276349Sneel int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]); 443276349Sneel if (l == -1) 444276349Sneel l = 0; 445276349Sneel if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) { 446282844Smav strcpy(buf + k, lbuf); 447282844Smav k += l; 448276349Sneel } 449276349Sneel } 450276349Sneel quote = 0; 451276349Sneel break; 452276349Sneel case _UC_DEFAULTCLASS: 453276349Sneel val = config.default_class ? config.default_class : ""; 454276349Sneel break; 455276349Sneel case _UC_MINUID: 456276349Sneel sprintf(buf, "%lu", (unsigned long) config.min_uid); 457276349Sneel quote = 0; 458276349Sneel break; 459276349Sneel case _UC_MAXUID: 460276349Sneel sprintf(buf, "%lu", (unsigned long) config.max_uid); 461276349Sneel quote = 0; 462276349Sneel break; 463276349Sneel case _UC_MINGID: 464276349Sneel sprintf(buf, "%lu", (unsigned long) config.min_gid); 465276349Sneel quote = 0; 466276349Sneel break; 467276349Sneel case _UC_MAXGID: 468276349Sneel sprintf(buf, "%lu", (unsigned long) config.max_gid); 469276349Sneel quote = 0; 470276349Sneel break; 471276349Sneel case _UC_EXPIRE: 472276349Sneel sprintf(buf, "%d", config.expire_days); 473276349Sneel quote = 0; 474276349Sneel break; 475256056Sgrehan case _UC_PASSWORD: 476256056Sgrehan sprintf(buf, "%d", config.password_days); 477256056Sgrehan quote = 0; 478256056Sgrehan break; 479256056Sgrehan case _UC_NONE: 480256056Sgrehan break; 481256056Sgrehan } 482256056Sgrehan 483256056Sgrehan if (comments[i]) 484256056Sgrehan fputs(comments[i], fp); 485256056Sgrehan 486256056Sgrehan if (*kwds[i]) { 487256056Sgrehan if (quote) 488280733Smav fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 489280733Smav else 490280733Smav fprintf(fp, "%s = %s\n", kwds[i], val); 491280733Smav#if debugging 492280733Smav printf("WROTE: %s = %s\n", kwds[i], val); 493256056Sgrehan#endif 494256056Sgrehan } 495256056Sgrehan } 496256056Sgrehan free(buf); 497256056Sgrehan return fclose(fp) != EOF; 498256056Sgrehan } 499256056Sgrehan } 500256056Sgrehan return 0; 501256056Sgrehan} 502256056Sgrehan