Raymond Toy pushed to branch issue-269-unix-get-user-homedir at cmucl / cmucl
Commits:
-
c78a982f
by Raymond Toy at 2023-11-26T14:32:34-08:00
-
089574ef
by Raymond Toy at 2023-11-26T14:51:49-08:00
-
b26975f6
by Raymond Toy at 2023-11-26T14:52:03-08:00
4 changed files:
Changes:
| ... | ... | @@ -2902,22 +2902,25 @@ |
| 2902 | 2902 | c-string))
|
| 2903 | 2903 | |
| 2904 | 2904 | (defun unix-get-user-homedir (name)
|
| 2905 | - _N"Get the user home directory for user named NAME. If there's no such
|
|
| 2906 | - user or if we don't have enough space to store the path, return NIL."
|
|
| 2907 | - (with-alien ((homedir (array c-call:char 1024)))
|
|
| 2905 | + _N"Get the user home directory for user named NAME. Two values are
|
|
| 2906 | + returned: the pathname of the home directory and a status code. If
|
|
| 2907 | + the home directory does not exist NIL is returned. The status is 0
|
|
| 2908 | + if no errors occurred. Otherwise a non-zero value is returned.
|
|
| 2909 | + Examining errno may give information about what failed."
|
|
| 2910 | + (with-alien ((status c-call:int))
|
|
| 2908 | 2911 | (let ((result
|
| 2909 | 2912 | (alien-funcall
|
| 2910 | 2913 | (extern-alien "os_get_user_homedir"
|
| 2911 | - (function c-call:int
|
|
| 2914 | + (function c-call:c-string
|
|
| 2912 | 2915 | c-call:c-string
|
| 2913 | - (* char)
|
|
| 2914 | - c-call:int))
|
|
| 2916 | + (* c-call:int)))
|
|
| 2915 | 2917 | name
|
| 2916 | - (cast homedir (* c-call:char))
|
|
| 2917 | - 1024)))
|
|
| 2918 | - (when (zerop result)
|
|
| 2919 | - (pathname
|
|
| 2920 | - (concatenate 'string
|
|
| 2921 | - (cast homedir c-call:c-string)
|
|
| 2922 | - "/"))))))
|
|
| 2918 | + (addr status))))
|
|
| 2919 | + (if (and (zerop status) result)
|
|
| 2920 | + (values (pathname
|
|
| 2921 | + (concatenate 'string
|
|
| 2922 | + result
|
|
| 2923 | + "/"))
|
|
| 2924 | + status)
|
|
| 2925 | + (values result status)))))
|
|
| 2923 | 2926 | |
| ... | ... | @@ -1434,7 +1434,10 @@ msgstr "" |
| 1434 | 1434 | |
| 1435 | 1435 | #: src/code/unix.lisp
|
| 1436 | 1436 | msgid ""
|
| 1437 | -"Get the user home directory for user named NAME. If there's no such\n"
|
|
| 1438 | -" user or if we don't have enough space to store the path, return NIL."
|
|
| 1437 | +"Get the user home directory for user named NAME. Two values are\n"
|
|
| 1438 | +" returned: the pathname of the home directory and a status code. If\n"
|
|
| 1439 | +" the home directory does not exist NIL is returned. The status is 0\n"
|
|
| 1440 | +" if no errors occurred. Otherwise a non-zero value is returned.\n"
|
|
| 1441 | +" Examining errno may give information about what failed."
|
|
| 1439 | 1442 | msgstr ""
|
| 1440 | 1443 |
| ... | ... | @@ -864,43 +864,65 @@ os_software_version(void) |
| 864 | 864 | return result;
|
| 865 | 865 | }
|
| 866 | 866 | |
| 867 | -int
|
|
| 868 | -os_get_user_homedir(const char* name, char* homedir, int len)
|
|
| 867 | +char *
|
|
| 868 | +os_get_user_homedir(const char* name, int *status)
|
|
| 869 | 869 | {
|
| 870 | - int rc;
|
|
| 871 | 870 | int buflen;
|
| 872 | 871 | char * buf;
|
| 873 | 872 | struct passwd pwd;
|
| 874 | 873 | struct passwd *result;
|
| 875 | 874 | |
| 876 | 875 | buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
|
| 876 | + /*
|
|
| 877 | + * If sysconf failed, just try some possibly large enough value
|
|
| 878 | + */
|
|
| 879 | + if (buflen == -1) {
|
|
| 880 | + buflen = 1024;
|
|
| 881 | + }
|
|
| 877 | 882 | |
| 878 | - buf = malloc(buflen);
|
|
| 883 | + /*
|
|
| 884 | + * sysconf may return a value that is not large enough, so start
|
|
| 885 | + * with the given value and keep increasing it until we reach some
|
|
| 886 | + * upper limit and give up.
|
|
| 887 | + */
|
|
| 888 | + while (buflen <= (1 << 20)) {
|
|
| 889 | + errno = 0;
|
|
| 890 | + buf = malloc(buflen);
|
|
| 879 | 891 | |
| 880 | - if (buf == NULL) {
|
|
| 881 | - return -1;
|
|
| 882 | - }
|
|
| 892 | + if (buf == NULL) {
|
|
| 893 | + *status = -1;
|
|
| 894 | + return NULL;
|
|
| 895 | + }
|
|
| 896 | + |
|
| 897 | + *status = getpwnam_r(name, &pwd, buf, buflen, &result);
|
|
| 883 | 898 | |
| 884 | - rc = getpwnam_r(name, &pwd, buf, buflen, &result);
|
|
| 899 | + if (*status == 0) {
|
|
| 900 | + /*
|
|
| 901 | + * Success, or entry was not found. If found the result
|
|
| 902 | + * is not NULL. Return the result or NULL
|
|
| 903 | + */
|
|
| 904 | + fprintf(stderr, "dir = %s\n", pwd.pw_dir);
|
|
| 905 | + return result ? strdup(pwd.pw_dir) : NULL;
|
|
| 906 | + }
|
|
| 885 | 907 | |
| 886 | - if ((rc == 0) && result != NULL) {
|
|
| 887 | 908 | /*
|
| 888 | - * Found a matching entry. Copy it to the output buffer if we
|
|
| 889 | - * have room. If not, set code to -1
|
|
| 909 | + * Check errno for ERANGE. If so, the buffer was too small, so grow it.
|
|
| 890 | 910 | */
|
| 891 | - if (strlen(pwd.pw_dir) < len) {
|
|
| 892 | - strcpy(homedir, pwd.pw_dir);
|
|
| 911 | + if (errno == ERANGE) {
|
|
| 912 | + free(buf);
|
|
| 913 | + buflen *= 2;
|
|
| 893 | 914 | } else {
|
| 894 | - rc = -1;
|
|
| 915 | + /*
|
|
| 916 | + * Some other error. Just return NULL
|
|
| 917 | + */
|
|
| 918 | + return NULL;
|
|
| 895 | 919 | }
|
| 896 | - } else {
|
|
| 897 | - rc = -1;
|
|
| 898 | 920 | }
|
| 899 | 921 | |
| 900 | - if (buf) {
|
|
| 901 | - free(buf);
|
|
| 902 | - }
|
|
| 903 | -
|
|
| 904 | - return rc;
|
|
| 922 | + /*
|
|
| 923 | + * Ran out of space. Just return NULL and set status to -1.
|
|
| 924 | + */
|
|
| 925 | + *status = -1;
|
|
| 926 | + return NULL;
|
|
| 905 | 927 | }
|
| 906 | 928 | |
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | (in-package "OS-TESTS")
|
| 5 | 5 | |
| 6 | -(define-test user-homedir
|
|
| 6 | +(define-test user-homedir.1
|
|
| 7 | 7 | "Test user-homedir"
|
| 8 | 8 | (:tag :issues)
|
| 9 | 9 | ;; Simple test to see if unix-get-user-homedir returns the expected
|
| ... | ... | @@ -16,9 +16,22 @@ |
| 16 | 16 | (let* ((info-dir (unix:user-info-dir user-info))
|
| 17 | 17 | (info-name (unix:user-info-name user-info))
|
| 18 | 18 | (expected-home-pathname (pathname
|
| 19 | - (concatenate 'string info-dir "/")))
|
|
| 20 | - (home-pathname (unix:unix-get-user-homedir info-name)))
|
|
| 21 | - (assert-true info-dir)
|
|
| 22 | - (assert-true info-name)
|
|
| 19 | + (concatenate 'string info-dir "/"))))
|
|
| 20 | + (multiple-value-bind (home-pathname status)
|
|
| 21 | + (unix:unix-get-user-homedir info-name)
|
|
| 22 | + (assert-true info-dir)
|
|
| 23 | + (assert-true info-name)
|
|
| 23 | 24 | |
| 24 | - (assert-equal home-pathname expected-home-pathname)))) |
|
| 25 | + (assert-equal home-pathname expected-home-pathname)
|
|
| 26 | + (assert-eql status 0)))))
|
|
| 27 | + |
|
| 28 | +(define-test user-homedir.2
|
|
| 29 | + "Test user-homedir"
|
|
| 30 | + (:tag :issues)
|
|
| 31 | + ;; Simple test to see if unix-get-user-homedir returns the expected
|
|
| 32 | + ;; value for a user that does not exist. Well, we assume such a
|
|
| 33 | + ;; user doesn't exist.
|
|
| 34 | + (multiple-value-bind (home-pathname status)
|
|
| 35 | + (unix:unix-get-user-homedir "zotuserunknown")
|
|
| 36 | + (assert-eql home-pathname nil)
|
|
| 37 | + (assert-eql status 0))) |