Raymond Toy pushed to branch master at cmucl / cmucl
Commits:
-
a6854bb6
by Raymond Toy at 2023-11-30T13:47:10+00:00
-
8e067da9
by Raymond Toy at 2023-11-30T13:47:20+00:00
6 changed files:
- src/code/exports.lisp
- src/code/os.lisp
- src/general-info/release-21f.md
- src/i18n/locale/cmucl-os.pot
- src/lisp/os-common.c
- + tests/os.lisp
Changes:
| ... | ... | @@ -2074,6 +2074,7 @@ |
| 2074 | 2074 | "FD-STREAM-P" "FIND-IF-IN-CLOSURE" "FOREIGN-SYMBOL-ADDRESS"
|
| 2075 | 2075 | "FOREIGN-SYMBOL-CODE-ADDRESS" "FOREIGN-SYMBOL-DATA-ADDRESS"
|
| 2076 | 2076 | "GET-PAGE-SIZE" "GET-SYSTEM-INFO"
|
| 2077 | + "GET-USER-HOMEDIR-PATHNAME"
|
|
| 2077 | 2078 | "IGNORE-INTERRUPT"
|
| 2078 | 2079 | "INT-SAP" "INVALIDATE-DESCRIPTOR" "IO-TIMEOUT"
|
| 2079 | 2080 | "LISP-STREAM" "LONG-FLOAT-RADIX" "LONG-WORDS"
|
| ... | ... | @@ -57,3 +57,33 @@ |
| 57 | 57 | (error (intl:gettext "Unix system call getrusage failed: ~A.")
|
| 58 | 58 | (unix:get-unix-error-msg utime)))
|
| 59 | 59 | (values utime stime major-fault))))
|
| 60 | + |
|
| 61 | +;;; GET-USER-HOMEDIR-PATHNAME -- Public
|
|
| 62 | +;;;
|
|
| 63 | +(defun get-user-homedir-pathname (name)
|
|
| 64 | + _N"Get the user home directory for user named NAME. Two values are
|
|
| 65 | + returned: the pathname of the home directory and a status code. If
|
|
| 66 | + the home directory does not exist NIL is returned. The status is 0
|
|
| 67 | + if no errors occurred. Otherwise a non-zero value is returned.
|
|
| 68 | + Examining errno may give information about what failed."
|
|
| 69 | + (alien:with-alien ((status c-call:int))
|
|
| 70 | + (let (result)
|
|
| 71 | + (unwind-protect
|
|
| 72 | + (progn
|
|
| 73 | + (setf result
|
|
| 74 | + (alien:alien-funcall
|
|
| 75 | + (alien:extern-alien "os_get_user_homedir"
|
|
| 76 | + (function (alien:* c-call:c-string)
|
|
| 77 | + c-call:c-string
|
|
| 78 | + (* c-call:int)))
|
|
| 79 | + name
|
|
| 80 | + (alien:addr status)))
|
|
| 81 | + (if (and (zerop status)
|
|
| 82 | + (not (alien:null-alien result)))
|
|
| 83 | + (values (pathname
|
|
| 84 | + (concatenate 'string
|
|
| 85 | + (alien:cast result c-call:c-string)
|
|
| 86 | + "/"))
|
|
| 87 | + status)
|
|
| 88 | + (values nil status)))
|
|
| 89 | + (alien:free-alien result))))) |
| ... | ... | @@ -38,6 +38,7 @@ public domain. |
| 38 | 38 | * ~~#249~~ Replace LEA instruction with simpler shorter instructions in arithmetic vops for x86
|
| 39 | 39 | * ~~#253~~ Block-compile list-to-hashtable and callers
|
| 40 | 40 | * ~~#258~~ Remove `get-page-size` from linux-os.lisp
|
| 41 | + * ~~#269~~ Add function to get user's home directory
|
|
| 41 | 42 | * Other changes:
|
| 42 | 43 | * Improvements to the PCL implementation of CLOS:
|
| 43 | 44 | * Changes to building procedure:
|
| ... | ... | @@ -33,3 +33,12 @@ msgstr "" |
| 33 | 33 | msgid "Unix system call getrusage failed: ~A."
|
| 34 | 34 | msgstr ""
|
| 35 | 35 | |
| 36 | +#: src/code/os.lisp
|
|
| 37 | +msgid ""
|
|
| 38 | +"Get the user home directory for user named NAME. Two values are\n"
|
|
| 39 | +" returned: the pathname of the home directory and a status code. If\n"
|
|
| 40 | +" the home directory does not exist NIL is returned. The status is 0\n"
|
|
| 41 | +" if no errors occurred. Otherwise a non-zero value is returned.\n"
|
|
| 42 | +" Examining errno may give information about what failed."
|
|
| 43 | +msgstr ""
|
|
| 44 | + |
| ... | ... | @@ -863,3 +863,74 @@ os_software_version(void) |
| 863 | 863 |
|
| 864 | 864 | return result;
|
| 865 | 865 | }
|
| 866 | + |
|
| 867 | +/*
|
|
| 868 | + * Return the home directory of the user named NAME. If the user does
|
|
| 869 | + * not exist, returns NULL. Also returns NULL if the home directory
|
|
| 870 | + * cannot be determined for any reason. The parameter STATUS is 0 if
|
|
| 871 | + * getpwnam_r was successful. Otherwise it is the return value from
|
|
| 872 | + * getpwnam_r or -1 if we ran out of memory for the buffer.
|
|
| 873 | + */
|
|
| 874 | +char *
|
|
| 875 | +os_get_user_homedir(const char* name, int *status)
|
|
| 876 | +{
|
|
| 877 | + int buflen;
|
|
| 878 | + char *buf = NULL;
|
|
| 879 | + struct passwd pwd;
|
|
| 880 | + struct passwd *result;
|
|
| 881 | + |
|
| 882 | + buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
|
|
| 883 | + /*
|
|
| 884 | + * If sysconf failed, just try some possibly large enough value.
|
|
| 885 | + */
|
|
| 886 | + if (buflen == -1) {
|
|
| 887 | + buflen = 1024;
|
|
| 888 | + }
|
|
| 889 | + |
|
| 890 | + /*
|
|
| 891 | + * sysconf may return a value that is not large enough, so start
|
|
| 892 | + * with the given value and keep increasing it until we reach some
|
|
| 893 | + * upper limit and give up.
|
|
| 894 | + */
|
|
| 895 | + while (buflen <= (1 << 20)) {
|
|
| 896 | + buf = realloc(buf, buflen);
|
|
| 897 | + |
|
| 898 | + if (buf == NULL) {
|
|
| 899 | + *status = -1;
|
|
| 900 | + return NULL;
|
|
| 901 | + }
|
|
| 902 | + |
|
| 903 | + *status = getpwnam_r(name, &pwd, buf, buflen, &result);
|
|
| 904 | + |
|
| 905 | + if (*status == 0) {
|
|
| 906 | + /*
|
|
| 907 | + * Success, or entry was not found. If found, the result
|
|
| 908 | + * is not NULL. Return the result or NULL
|
|
| 909 | + */
|
|
| 910 | + char* path = result ? strdup(pwd.pw_dir) : NULL;
|
|
| 911 | + free(buf);
|
|
| 912 | + return path;
|
|
| 913 | + }
|
|
| 914 | + |
|
| 915 | + /*
|
|
| 916 | + * Check errno for ERANGE. If so, the buffer was too small, so grow it.
|
|
| 917 | + */
|
|
| 918 | + if (errno == ERANGE) {
|
|
| 919 | + buflen *= 2;
|
|
| 920 | + } else {
|
|
| 921 | + /*
|
|
| 922 | + * Some other error. Just return NULL
|
|
| 923 | + */
|
|
| 924 | + free(buf);
|
|
| 925 | + return NULL;
|
|
| 926 | + }
|
|
| 927 | + }
|
|
| 928 | + |
|
| 929 | + /*
|
|
| 930 | + * Ran out of space. Just return NULL and set status to -1.
|
|
| 931 | + */
|
|
| 932 | + free(buf);
|
|
| 933 | + *status = -1;
|
|
| 934 | + return NULL;
|
|
| 935 | +}
|
|
| 936 | + |
| 1 | +(defpackage :os-tests
|
|
| 2 | + (:use :cl :lisp-unit))
|
|
| 3 | + |
|
| 4 | +(in-package "OS-TESTS")
|
|
| 5 | + |
|
| 6 | + |
|
| 7 | +(define-test user-homedir.1
|
|
| 8 | + "Test user-homedir"
|
|
| 9 | + (:tag :issues)
|
|
| 10 | + ;; Simple test to see if get-user-homedir-pathname returns the
|
|
| 11 | + ;; expected value. Use getuid and getpwuid to figure out what the
|
|
| 12 | + ;; name and home directory should be.
|
|
| 13 | + (let* ((uid (unix:unix-getuid))
|
|
| 14 | + (user-info (unix:unix-getpwuid uid)))
|
|
| 15 | + (assert-true uid)
|
|
| 16 | + (assert-true user-info)
|
|
| 17 | + (let* ((info-dir (unix:user-info-dir user-info))
|
|
| 18 | + (info-name (unix:user-info-name user-info))
|
|
| 19 | + (expected-home-pathname (pathname
|
|
| 20 | + (concatenate 'string info-dir "/"))))
|
|
| 21 | + (multiple-value-bind (home-pathname status)
|
|
| 22 | + (system:get-user-homedir-pathname info-name)
|
|
| 23 | + (assert-true info-dir)
|
|
| 24 | + (assert-true info-name)
|
|
| 25 | + |
|
| 26 | + (assert-equal home-pathname expected-home-pathname)
|
|
| 27 | + (assert-eql status 0)))))
|
|
| 28 | + |
|
| 29 | +(define-test user-homedir.2
|
|
| 30 | + "Test user-homedir"
|
|
| 31 | + (:tag :issues)
|
|
| 32 | + ;; Simple test to see if get-user-homedir-pathname returns the expected
|
|
| 33 | + ;; value for a user that does not exist. Well, we assume such a
|
|
| 34 | + ;; user doesn't exist.
|
|
| 35 | + (multiple-value-bind (home-pathname status)
|
|
| 36 | + (system:get-user-homedir-pathname "zotuserunknown")
|
|
| 37 | + (assert-eql home-pathname nil)
|
|
| 38 | + (assert-eql status 0))) |