-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathos.lisp
159 lines (140 loc) · 4.23 KB
/
os.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
(defpackage :kiln/os
(:documentation "Utilities for OS interface")
(:use
:cl
:alexandria
:cmd
:serapeum)
(:import-from :cffi)
(:import-from :cmd)
(:import-from :kiln/dispatch :exec :exit)
(:import-from :kiln/flags :dbg)
(:import-from
:uiop
:file-exists-p
:directory-exists-p
:getenv
:getenvp
:hostname)
(:shadow
;; TODO Remove when Quicklisp updates
:parse-cmd-dsl)
(:export
:chdir
:directory-exists-p
:exec
:exit
:file-exists-p
:getcwd
:getenv
:getenvp
:getpid
:hostname
:os-linux-p
:setpgrp
:with-chdir
:with-save-directory))
(in-package :kiln/os)
#.(if (uiop:os-unix-p)
'(cffi:defcfun "setpgrp" :int)
'(defun setpgrp ()))
(defun os-linux-p ()
(and (uiop:os-unix-p)
(search "linux" ($cmd "uname") :test #'char-equal)))
(setf (documentation #'setpgrp 'function)
"Make this process the leader of a new process group.")
(defun chdir (&optional (dir (user-homedir-pathname)))
"Set the operating system directory and sync
`*default-pathname-defaults*' to it."
(let ((dir (cmd::resolve-dir dir)))
(uiop:chdir dir)
(setf *default-pathname-defaults* dir)))
(defun getcwd ()
(uiop:getcwd))
(defun (setf getcwd) (dir)
(chdir dir))
(defun call/save-directory (fn)
(let ((start-dir (uiop:getcwd)))
(unwind-protect
(funcall fn)
(chdir start-dir))))
(defmacro with-save-directory ((&key) &body body)
"Run BODY, restoring the current directory afterwards."
(with-thunk (body)
`(call/save-directory ,body)))
(defmacro with-chdir ((dir) &body body)
"Set current directory to DIR, run BODY, restore current directory."
`(with-save-directory ()
(chdir ,dir)
,@body))
(cffi:defcfun (%execv "execv") :int
(path :string)
(args (:pointer :string)))
(defun execv (executable arglist)
(assert (every #'stringp arglist))
(cffi:with-foreign-string (path executable)
(let ((argv
;; We don't care about cleanup, the end is nigh.
(cffi:foreign-alloc :string
:initial-contents
arglist
:null-terminated-p t)))
(when-let (code (%execv path argv))
(error "execv(3) returned: ~a" code)))))
(defun execvp (executable arglist)
(let ((executable
(namestring
(resolve-executable executable))))
(execv executable arglist)))
(defun exec-no-dsl (command &key (unwind t))
(dbg "Replacing current process with ~{~a~^ ~}"
command)
(if unwind
(throw 'exec
(lambda ()
(exec-no-dsl command :unwind nil)))
(execvp (car command) command)))
;;; TODO Remove when Quicklisp updates.
(defun parse-cmd-dsl (command)
(let ((cmd (cmd::parse-cmd command)))
(values (cmd::flatten-string-tokens (cmd::cmd-argv cmd))
(cmd::flatten-string-tokens (cmd::cmd-kwargs cmd)))))
(defun exec (&rest command)
"Replace the current process with COMMAND.
COMMAND is parsed as if by `cmd:cmd'.
Unless `:unwind nil` is passed as part of COMMAND, `unwind-protect'
forms in the user's program are run before the current process is
replaced. Otherwise the program is simply replaced without unwinding.
This is like the `exec' shell built-in, rather than `execvp(3)`, in
that `arg0' is set automatically."
(mvlet* ((command kwargs (parse-cmd-dsl command))
(unwind (getf kwargs :unwind t))
(kwargs (remove-from-plist kwargs :unwind)))
(when kwargs
(error "Keyword arguments not supported by ~s: ~a"
'exec
kwargs))
(exec-no-dsl command :unwind unwind)))
(defun exit (&key (code 0) (unwind t))
"Exit with code CODE."
(check-type code integer)
(if unwind
(throw 'exit code)
(uiop:quit code)))
(defun getpid ()
;; Adapted from the sources of Sly. Should there be a trivial-getpid
;; library?
#+ccl (ccl::getpid)
#+sbcl (sb-posix:getpid)
#+ecl (ext:getpid)
#+clisp (os:process-id)
#+cmucl (unix:unix-getpid)
#+abcl (ext:get-pid)
#+allegro (excl.osi:getpid)
#+(and lispworks win32) (win32:get-current-process-id)
#+(and lispworks (not win32))
(system::getpid)
#+mkcl (mkcl:getpid)
#+scl (unix:unix-getpid)
#+clasp (si:getpid)
#+cormanlisp ccl:*current-process-id*)