Subject: pw_copy() and error handling
To: NetBSD Userlevel Technical Discussion List <tech-userlevel@netbsd.org>
From: Jason Thorpe <thorpej@wasabisystems.com>
List: tech-userlevel
Date: 08/02/2004 11:47:57
--Apple-Mail-14--651355829
Content-Type: multipart/mixed; boundary=Apple-Mail-13--651355849


--Apple-Mail-13--651355849
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

Hi folks...

I'm working on an application that uses the password file routines in 
libutil, and have found pw_copy()'s error handling to be less than 
ideal.  Specifically, it writes an error message to stderr and then 
exits the process.  What I need to do is return an error condition to 
my application, which will then perform its own cleanup procedures.

What I did was morph pw_copy() into a new pw_copyx() that returns a 
success / failure indication, and fills in a buffer with the error 
message.  I then added a pw_copy() wrapper around the new pw_copyx() 
that mimics the old behavior.

Patch attached.  Does anyone object to me committing it?

         -- Jason R. Thorpe <thorpej@wasabisystems.com>

--Apple-Mail-13--651355849
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	x-unix-mode=0644;
	name="pw_copyx-patch.txt"
Content-Disposition: attachment;
	filename=pw_copyx-patch.txt

Index: include/util.h
===================================================================
RCS file: /cvsroot/src/include/util.h,v
retrieving revision 1.31
diff -u -p -r1.31 util.h
--- include/util.h	7 Aug 2003 09:44:11 -0000	1.31
+++ include/util.h	2 Aug 2004 18:41:03 -0000
@@ -82,6 +82,8 @@ int		pidfile(const char *);
 int		pidlock(const char *, int, pid_t *, const char *);
 int		pw_abort(void);
 void		pw_copy(int, int, struct passwd *, struct passwd *);
+int		pw_copyx(int, int, struct passwd *, struct passwd *,
+			 char *, size_t);
 void		pw_edit(int, const char *);
 void		pw_error(const char *, int, int);
 void		pw_getconf(char *, size_t, const char *, const char *);
Index: lib/libutil/passwd.c
===================================================================
RCS file: /cvsroot/src/lib/libutil/passwd.c,v
retrieving revision 1.35
diff -u -p -r1.35 passwd.c
--- lib/libutil/passwd.c	7 Aug 2003 16:44:59 -0000	1.35
+++ lib/libutil/passwd.c	2 Aug 2004 18:41:04 -0000
@@ -320,77 +320,118 @@ pw_equal(char *buf, struct passwd *pw)
 void
 pw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw)
 {
+	char errbuf[200];
+	int rv;
+
+	rv = pw_copyx(ffd, tfd, pw, old_pw, errbuf, sizeof(errbuf));
+	if (rv == 0) {
+		warnx("%s", errbuf);
+		pw_error(NULL, 0, 1);
+	}
+}
+
+int
+pw_copyx(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw,
+    char *errbuf, size_t errbufsz)
+{
 	const char *filename;
 	char mpwd[MAXPATHLEN], mpwdl[MAXPATHLEN], *p, buf[8192];
 	FILE *from, *to;
 	int done;
 
 	_DIAGASSERT(pw != NULL);
+	_DIAGASSERT(errbuf != NULL);
 	/* old_pw may be NULL */
 
-	if ((filename = pw_filename(_PATH_MASTERPASSWD)) == NULL)
-		pw_error(pw_prefix, 1,1);
+	if ((filename = pw_filename(_PATH_MASTERPASSWD)) == NULL) {
+		snprintf(errbuf, errbufsz, "%s: %s", pw_prefix,
+		    strerror(errno));
+		return (0);
+	}
 	(void)strcpy(mpwd, filename);
-	if ((filename = pw_filename(_PATH_MASTERPASSWD_LOCK)) == NULL)
-		pw_error(pw_prefix, 1,1);
+	if ((filename = pw_filename(_PATH_MASTERPASSWD_LOCK)) == NULL) {
+		snprintf(errbuf, errbufsz, "%s: %s", pw_prefix,
+		    strerror(errno));
+		return (0);
+	}
 	(void)strcpy(mpwdl, filename);
 
-	if (!(from = fdopen(ffd, "r")))
-		pw_error(mpwd, 1, 1);
-	if (!(to = fdopen(tfd, "w")))
-		pw_error(mpwdl, 1, 1);
+	if (!(from = fdopen(ffd, "r"))) {
+		snprintf(errbuf, errbufsz, "%s: %s", mpwd, strerror(errno));
+		return (0);
+	}
+	if (!(to = fdopen(tfd, "w"))) {
+		snprintf(errbuf, errbufsz, "%s: %s", mpwdl, strerror(errno));
+		return (0);
+	}
 
 	for (done = 0; fgets(buf, sizeof(buf), from);) {
 		if (!strchr(buf, '\n')) {
-			warnx("%s: line too long", mpwd);
-			pw_error(NULL, 0, 1);
+			snprintf(errbuf, errbufsz, "%s: line too long", mpwd);
+			return (0);
 		}
 		if (done) {
 			(void)fprintf(to, "%s", buf);
-			if (ferror(to))
-				goto err;
+			if (ferror(to)) {
+				snprintf(errbuf, errbufsz, "%s",
+				    strerror(errno));
+				return (0);
+			}
 			continue;
 		}
 		if (!(p = strchr(buf, ':'))) {
-			warnx("%s: corrupted entry", mpwd);
-			pw_error(NULL, 0, 1);
+			snprintf(errbuf, errbufsz, "%s: corrupted entry", mpwd);
+			return (0);
 		}
 		*p = '\0';
 		if (strcmp(buf, pw->pw_name)) {
 			*p = ':';
 			(void)fprintf(to, "%s", buf);
-			if (ferror(to))
-				goto err;
+			if (ferror(to)) {
+				snprintf(errbuf, errbufsz, "%s",
+				    strerror(errno));
+				return (0);
+			}
 			continue;
 		}
 		*p = ':';
 		if (old_pw && !pw_equal(buf, old_pw)) {
-			warnx("%s: entry inconsistent", mpwd);
-			pw_error(NULL, 0, 1);
+			snprintf(errbuf, errbufsz, "%s: entry inconsistent",
+			    mpwd);
+			return (0);
 		}
 		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
 		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
 		    pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire,
 		    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
 		done = 1;
-		if (ferror(to))
-			goto err;
+		if (ferror(to)) {
+			snprintf(errbuf, errbufsz, "%s", strerror(errno));
+			return (0);
+		}
 	}
 	/* Only append a new entry if real uid is root! */
 	if (!done) {
-		if (getuid() == 0)
+		if (getuid() == 0) {
 			(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
 			    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
 			    pw->pw_class, (long)pw->pw_change,
 			    (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir,
 			    pw->pw_shell);
-		else
-			warnx("%s: changes not made, no such entry", mpwd);
+			done = 1;
+		} else {
+			snprintf(errbuf, errbufsz,
+			    "%s: changes not made, no such entry", mpwd);
+		}
 	}
 
-	if (ferror(to))
-err:		pw_error(NULL, 1, 1);
+	if (ferror(to)) {
+		snprintf(errbuf, errbufsz, "%s", strerror(errno));
+		return (0);
+	}
 	(void)fclose(to);
+
+	return (done);
 }
 
 void
Index: lib/libutil/pw_init.3
===================================================================
RCS file: /cvsroot/src/lib/libutil/pw_init.3,v
retrieving revision 1.12
diff -u -p -r1.12 pw_init.3
--- lib/libutil/pw_init.3	7 Aug 2003 16:45:00 -0000	1.12
+++ lib/libutil/pw_init.3	2 Aug 2004 18:41:04 -0000
@@ -31,7 +31,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 15, 1995
+.Dd August 1, 2004
 .Dt PW_INIT 3
 .Os
 .Sh NAME
@@ -39,6 +39,7 @@
 .Nm pw_edit ,
 .Nm pw_prompt ,
 .Nm pw_copy ,
+.Nm pw_copyx ,
 .Nm pw_scan ,
 .Nm pw_error
 .Nd utility functions for interactive passwd file updates
@@ -56,6 +57,9 @@
 .Ft void
 .Fn pw_copy "int ffd" "int tfd" "struct passwd *pw" "struct passwd *old_pw"
 .Ft int
+.Fn pw_copyx "int ffd" "int tfd" "struct passwd *pw" "struct passwd *old_pw" \
+    "char *errbuf" "size_t errbufsz"
+.Ft int
 .Fn pw_scan "char *bp" "struct passwd *pw" "int *flags"
 .Ft void
 .Fn pw_error "const char *name" "int err" "int eval"
@@ -115,7 +119,24 @@ or the process is aborted.
 If an entry is not found to match
 .Fa pw ,
 a new entry is appended to the passwd file only if the real user
-ID is 0.
+ID is 0.  If an error occurs,
+.Fn pw_copy
+will display a message on
+.Dv stderr
+and call
+.Fn pw_error .
+.Pp
+The
+.Fn pw_copyx
+function performs the same operation as
+.Fn pw_copy
+with the exception of error handling.
+Upon an error,
+.Fn pw_copyx
+will write an error message into the buffer pointed to by
+.Fa errbuf
+which has the size
+.Fa errbufsz .
 .Pp
 The
 .Fn pw_scan
@@ -182,6 +203,11 @@ The process exits with status
 .Fa eval .
 .Sh RETURN VALUES
 The
+.Fn pw_copyx
+function returns 1 if the new password entry was successfully written
+to the destination file, and 0 otherwise.
+.Pp
+The
 .Fn pw_scan
 function prints a warning message and returns 0 if the string in the
 .Fa bp

--Apple-Mail-13--651355849--

--Apple-Mail-14--651355829
content-type: application/pgp-signature; x-mac-type=70674453;
	name=PGP.sig
content-description: This is a digitally signed message part
content-disposition: inline; filename=PGP.sig
content-transfer-encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (Darwin)

iD4DBQFBDoxdOpVKkaBm8XkRArf9AJ43hzM19tNGVgLjZISmLwH/KeaW9ACWMSU/
EFUTXQ4aft3LBxYiQD4hPw==
=Kt9w
-----END PGP SIGNATURE-----

--Apple-Mail-14--651355829--