[Git][cmucl/cmucl][master] 2 commits: Fix #408: Add C functions to get user name and home dir
Raymond Toy pushed to branch master at cmucl / cmucl Commits: 420d3ee2 by Raymond Toy at 2025-11-13T13:38:01-08:00 Fix #408: Add C functions to get user name and home dir - - - - - 143a7651 by Raymond Toy at 2025-11-13T13:38:01-08:00 Merge branch 'issue-408-get-user-name-and-homedir' into 'master' Fix #408: Add C functions to get user name and home dir Closes #408, #373, and #375 See merge request cmucl/cmucl!300 - - - - - 6 changed files: - src/code/filesys.lisp - src/code/os.lisp - src/code/unix.lisp - src/i18n/locale/cmucl-unix.pot - src/lisp/Darwin-os.c - src/lisp/os-common.c Changes: ===================================== src/code/filesys.lisp ===================================== @@ -1232,8 +1232,7 @@ optionally keeping some of the most recent old versions." (declare (ignore sec min hour date month)) (format t "~2D ~8A ~8D ~12A ~A~@[/~]~%" nlink - (let ((user-info (unix:unix-getpwuid uid))) - (if user-info (unix:user-info-name user-info) uid)) + (or (unix::unix-get-username uid) uid) size (decode-universal-time-for-files mtime year) tail ===================================== src/code/os.lisp ===================================== @@ -69,29 +69,21 @@ The status is 0 if no errors occurred. Otherwise a non-zero value is returned. Examining errno may give information about what failed." (declare (string name)) - (cond - ((zerop (length name)) - (multiple-value-bind (user-info status) - (unix:unix-getpwuid (unix:unix-getuid)) - (values (when user-info - (unix:user-info-dir user-info)) - status))) - (t - (alien:with-alien ((status c-call:int)) - (let (result) - (unwind-protect - (progn - (setf result - (alien:alien-funcall - (alien:extern-alien "os_get_user_homedir" - (function (alien:* c-call:c-string) - c-call:c-string - (* c-call:int))) - name - (alien:addr status))) - (if (and (zerop status) - (not (alien:null-alien result))) - (values (alien:cast result c-call:c-string) - status) - (values nil status))) - (alien:free-alien result))))))) + (alien:with-alien ((status c-call:int)) + (let (result) + (unwind-protect + (progn + (setf result + (alien:alien-funcall + (alien:extern-alien "os_get_user_homedir" + (function (alien:* c-call:c-string) + c-call:c-string + (* c-call:int))) + name + (alien:addr status))) + (if (and (zerop status) + (not (alien:null-alien result))) + (values (alien:cast result c-call:c-string) + status) + (values nil status))) + (alien:free-alien result))))) ===================================== src/code/unix.lisp ===================================== @@ -2541,3 +2541,28 @@ ;; Result from strerror can be localized so we need to decode ;; those octets to get a proper Lisp string. (string-decode (cast result c-string) :default))) + +(defun unix-get-username (uid) + _N"Returns a string that is the user name corresponding to the given UID. + If no such uid exists or if the user name does not exist, NIL is + returned." + (with-alien ((status c-call:int)) + (let (result) + (unwind-protect + (progn + (setf result + (alien-funcall + (extern-alien "os_get_user_name" + (function (* c-call:c-string) + uid-t + (* c-call:int))) + uid + (addr status))) + (values + (if (and (zerop status) + (not (null-alien result))) + (cast result c-call:c-string) + nil) + status)) + (free-alien name))))) + ===================================== src/i18n/locale/cmucl-unix.pot ===================================== @@ -810,3 +810,10 @@ msgstr "" msgid "Returns a string that describes the error code Errno" msgstr "" +#: src/code/unix.lisp +msgid "" +"Returns a string that is the user name corresponding to the given UID.\n" +" If no such uid exists or if the user name does not exist, NIL is\n" +" returned." +msgstr "" + ===================================== src/lisp/Darwin-os.c ===================================== @@ -593,7 +593,7 @@ os_temporary_directory(void) len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, PATH_MAX); if (len == 0 || len > PATH_MAX || (len == PATH_MAX && path[len - 1] != '/')) { - strlcpy(path, "/tmp/"); + strcpy(path, "/tmp/"); } else if (path[len - 1] != '/') { strcat(path, "/"); } ===================================== src/lisp/os-common.c ===================================== @@ -734,58 +734,97 @@ os_lstat(const char* path, uint64_t *dev, uint64_t *ino, unsigned int *mode, uin } /* - * Interface for file-author. Given a pathname, returns a new string - * holding the author of the file or NULL if some error occurred. The - * caller is responsible for freeing the memory used by the string. + * Basic interface to getpwuid, except we will automatically retry if + * the given buffer is too small. To support this |buffer| is a + * pointer to a char buffer. This will be updated with a new buffer + * if we needed to retry getpwuid with a larger buffer. The remaining + * parameters are the same as for getpwuid. */ -char * -os_file_author(const char *path) +static int +os_getpwuid(uid_t uid, struct passwd *pwd, char **buffer, size_t buflen, struct passwd **result) { - struct stat sb; - char initial[1024]; - char *buffer, *obuffer; - size_t size; - struct passwd pwd; - struct passwd *ppwd; - char *result; - - if (stat(path, &sb) != 0) { - return NULL; - } - - result = NULL; - buffer = initial; - obuffer = NULL; - size = sizeof(initial) / sizeof(initial[0]); + int status; + char *obuffer = NULL; /* * Keep trying with larger buffers until a maximum is reached. We * assume (1 << 20) is large enough for any OS. */ again: - switch (getpwuid_r(sb.st_uid, &pwd, buffer, size, &ppwd)) { + switch (status = getpwuid_r(uid, pwd, *buffer, buflen, result)) { case 0: /* Success, though we might not have a matching entry */ - result = (ppwd == NULL) ? NULL : strdup(pwd.pw_name); break; case ERANGE: /* Buffer is too small, double its size and try again */ - size *= 2; - if (size > (1 << 20)) { + buflen *= 2; + if (buflen > (1 << 20)) { break; } - if ((buffer = realloc(obuffer, size)) == NULL) { + if ((*buffer = realloc(obuffer, buflen)) == NULL) { break; } - obuffer = buffer; + obuffer = *buffer; goto again; default: /* All other errors */ break; } free(obuffer); - - return result; + + return status; +} + + +/* + * Given the UID, return the user name. If the uid does not exist, + * returns NULL. The caller must call free on the string that is + * returned. + */ +char * +os_get_user_name(uid_t uid, int *status) +{ + char *buf; + char *name; + struct passwd pwd; + struct passwd *result; + char buffer[1024]; + + buf = buffer; + + *status = os_getpwuid(uid, &pwd, &buf, sizeof(buffer), &result); + + if (*status != 0 || result == NULL || result->pw_name == NULL) { + name = NULL; + } else { + name = strdup(result->pw_name); + if (name == NULL) { + *status = errno; + } + if (buf != buffer) { + free(buf); + } + } + + return name; +} + +/* + * Interface for file-author. Given a pathname, returns a new string + * holding the author of the file or NULL if some error occurred. The + * caller is responsible for freeing the memory used by the string. + */ +char * +os_file_author(const char *path) +{ + int status; + struct stat sb; + + if (stat(path, &sb) != 0) { + return NULL; + } + + return os_get_user_name(sb.st_uid, &status); } int @@ -874,15 +913,8 @@ os_software_version(void) return result; } -/* - * Return the home directory of the user named NAME. If the user does - * not exist, returns NULL. Also returns NULL if the home directory - * cannot be determined for any reason. The parameter STATUS is 0 if - * getpwnam_r was successful. Otherwise it is the return value from - * getpwnam_r or -1 if we ran out of memory for the buffer. - */ -char * -os_get_user_homedir(const char* name, int *status) +static char * +get_homedir_by_user_name(const char* name, int *status) { int buflen; char *buf = NULL; @@ -944,6 +976,60 @@ os_get_user_homedir(const char* name, int *status) return NULL; } +/* + * Return the home directory for the given UID. If the home directory + * does not exist, NULL is returned. STATUS is set to 0 if the home + * directory could be found. Otherwise, it is the errno. + * + * The caller must free the memory returned. + */ +static char * +get_homedir_by_uid(uid_t uid, int *status) +{ + char buffer[1024]; + char *buf; + char *dir; + struct passwd pwd; + struct passwd *result; + + buf = buffer; + dir = NULL; + + *status = os_getpwuid(uid, &pwd, &buf, sizeof(buffer), &result); + + if (*status == 0 && result != NULL && result->pw_dir != NULL) { + dir = strdup(result->pw_dir); + if (dir == NULL) { + *status = errno; + } + if (buf != buffer) { + free(buf); + } + } + + return dir; +} + +/* + * Return the home directory of the user named NAME. If NAME is the + * empty string, returns the home directory of the current user. If + * the user does not exist, returns NULL. Also returns NULL if the + * home directory cannot be determined for any reason. The parameter + * STATUS is 0 if the home directory could be determined. Otherwise + * it is the errno value. + * + * The caller must free the memory returned for the result. + */ +char * +os_get_user_homedir(const char* name, int *status) +{ + if (strlen(name) == 0) { + return get_homedir_by_uid(getuid(), status); + } + + return get_homedir_by_user_name(name, status); +} + /* * Return a new string (or NULL) for the current working directory. * The caller must free this space. View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/f5484369b32ca4537618d6a... -- View it on GitLab: https://gitlab.common-lisp.net/cmucl/cmucl/-/compare/f5484369b32ca4537618d6a... You're receiving this email because of your account on gitlab.common-lisp.net.
participants (1)
-
Raymond Toy (@rtoy)