Raymond Toy pushed to branch issue-266-tilde-pathname-support 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
-
8fe17e76
by Raymond Toy at 2023-11-30T05:48:49-08: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))) |