lserver.lisp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. ;;;; lserver.lisp
  2. (in-package #:lserver-impl)
  3. (defparameter *lserver-home* (merge-pathnames #p".lserver/" (user-homedir-pathname)))
  4. (defparameter *default-socket* #p"default")
  5. (defparameter *directories* '(#p"tmp/"))
  6. (defclass lserver ()
  7. ((home :reader server-home :initarg :home :initform *lserver-home*)
  8. (socket :reader socket)
  9. (socket-file :reader socket-file :initarg :socket-file :initform *default-socket*)))
  10. (defgeneric rc-file (server))
  11. (defmethod rc-file ((server lserver))
  12. (merge-pathnames #p"lserverrc.lisp" (server-home server)))
  13. (defgeneric socket-address (server))
  14. (defmethod socket-address ((server lserver))
  15. (namestring (merge-pathnames (socket-file server) (merge-pathnames #p"tmp/" (server-home server)))))
  16. (defun make-server (&key (home *lserver-home*)
  17. (socket-file *default-socket*))
  18. (make-instance 'lserver :home home :socket-file socket-file))
  19. (defun server-shutdown (server)
  20. (let ((socket (socket server)))
  21. (sb-bsd-sockets:socket-shutdown socket :direction :io)
  22. (sb-bsd-sockets:socket-close socket)))
  23. (defun setup-directories (home)
  24. (dolist (dir *directories*)
  25. (ensure-directories-exist (merge-pathnames dir home))))
  26. ;;; TODO reset the socket?
  27. (defun setup-server (server)
  28. (setup-directories (server-home server))
  29. (let ((rc-file (rc-file server)))
  30. (if (uiop:file-exists-p rc-file)
  31. (load rc-file)
  32. (warn "Initialization file ~A does not exist." rc-file)))
  33. (unless (slot-boundp server 'socket)
  34. (setf (slot-value server 'socket) (setup-socket (socket-address server))))
  35. server)
  36. (defun start-server (server)
  37. (catch 'stop-server
  38. (unwind-protect
  39. (loop for sock = (sb-bsd-sockets:socket-accept (socket server))
  40. do (bt:make-thread (default-connection-handler sock) :name "lserver worker"))
  41. (delete-file (socket-address server)))))
  42. (defvar *arguments* nil)
  43. (defvar *client-name* "")
  44. (defparameter *handler* (lambda () (standard-handler *arguments*)))
  45. (defun default-connection-handler (socket)
  46. (lambda ()
  47. (let ((*interpreter-stream* (sb-bsd-sockets:socket-make-stream socket
  48. :input t
  49. :output t
  50. :element-type '(unsigned-byte 8)
  51. :auto-close t)))
  52. (let ((*standard-input* (flexi-streams:make-flexi-stream (make-server-binary-input-stream *interpreter-stream*)
  53. :external-format :utf-8))
  54. (*standard-output*
  55. (flexi-streams:make-flexi-stream
  56. (make-server-binary-output-stream *interpreter-stream*
  57. :stdout
  58. (if (isatty)
  59. :line-buffered
  60. :fully-buffered))
  61. :external-format :utf-8))
  62. (*error-output*
  63. (flexi-streams:make-flexi-stream
  64. (make-server-binary-output-stream *interpreter-stream*
  65. :stderr
  66. :unbuffered)
  67. :external-format :utf-8))
  68. (*query-io*
  69. (make-two-way-stream
  70. (flexi-streams:make-flexi-stream (make-server-binary-input-stream *interpreter-stream*)
  71. :external-format :utf-8)
  72. (flexi-streams:make-flexi-stream
  73. (make-server-binary-output-stream *interpreter-stream*
  74. :stderr
  75. :line-buffered)
  76. :external-format :utf-8)))
  77. (*default-pathname-defaults* (pathname (getcwd)))
  78. (*arguments* (argv))
  79. (*client-name* (argv0))
  80. (*package* (find-package "LSERVER"))
  81. code)
  82. (unwind-protect
  83. (progn
  84. ;;; if we can't pass the errors to the client, something must be wrong with the connection, so we don't care
  85. (ignore-errors
  86. ;;; the handler-case assumes we are able to tell the client something about the error
  87. (handler-case
  88. (let ((result (funcall *handler*)))
  89. (setf code (typecase result
  90. (integer result)
  91. (null 1)
  92. (t 0))))
  93. (simple-error (c) (princ c *error-output*))
  94. (file-error (c) (format *error-output* "Error with file ~A.~%" (file-error-pathname c)))
  95. (error () (sb-debug:print-backtrace :stream *error-output*))))
  96. (ignore-errors (finish-output *standard-output*))
  97. (ignore-errors (finish-output *error-output*))
  98. (ignore-errors (finish-output *query-io*))
  99. (ignore-errors (quit-interpreter (or code 255))))
  100. (ignore-errors (close *interpreter-stream*))
  101. (ignore-errors (sb-bsd-sockets:socket-close socket)))))))
  102. ;;; TODO permissions
  103. ;;; TODO treat socket directories & socket names separately
  104. (defun setup-socket (file)
  105. (ensure-directories-exist (pathname file))
  106. (uiop:delete-file-if-exists file)
  107. (let ((socket (make-instance 'sb-bsd-sockets:local-socket :type :stream)))
  108. (sb-bsd-sockets:socket-bind socket file)
  109. (sb-bsd-sockets:socket-listen socket 5)
  110. socket))
  111. (defstruct command name function description)
  112. (defstruct command-set
  113. (name "command set")
  114. (lock (bt:make-lock "command lock"))
  115. (commands (make-hash-table :test 'equal)))
  116. (defvar *commands* (make-command-set))
  117. (defvar *server-commands* (make-command-set))
  118. (defun add-command (name function &optional description (commands *commands*))
  119. (check-type name string)
  120. (check-type function (or symbol function))
  121. (check-type description (or string null))
  122. (unless description
  123. (setf description (documentation function 'function)))
  124. (let ((command (make-command :name name
  125. :function function
  126. :description description)))
  127. (bt:with-lock-held ((command-set-lock commands))
  128. (setf (gethash name (command-set-commands commands)) command))))
  129. (defun remove-command (name &optional (commands *commands*))
  130. (bt:with-lock-held ((command-set-lock commands))
  131. (remhash name (command-set-commands commands))))
  132. (defun get-command (name &optional (commands *commands*))
  133. (bt:with-lock-held ((command-set-lock commands))
  134. (values (gethash name (command-set-commands commands)))))
  135. (defun standard-handler (arguments)
  136. (if arguments
  137. (let* ((command-name (first arguments))
  138. (args (rest arguments))
  139. (command (or (get-command command-name)
  140. (get-command command-name *server-commands*))))
  141. (if command
  142. (funcall (command-function command) args)
  143. (error "Unknown command: ~A.~%" command-name)))
  144. (error "Command missing.~%")))
  145. (defun print-commands (&rest command-sets)
  146. (let ((cmds (loop for command-set in command-sets
  147. append (bt:with-lock-held ((command-set-lock command-set))
  148. (sort (loop for command being the hash-values of (command-set-commands command-set)
  149. collect (list (command-name command) (command-description command)))
  150. #'string<
  151. :key #'first)))))
  152. (let* ((max-command-length (loop for (name description) in cmds maximize (length name)))
  153. (format-string (format nil "~~{~~{~~&~~A~~@[~~~DT~~A~~]~~%~~}~~}" (+ max-command-length 2))))
  154. (format t format-string cmds))))
  155. (add-command "--list-commands" (lambda (args)
  156. (declare (ignore args))
  157. (print-commands *commands*)
  158. t)
  159. "List available commands"
  160. *server-commands*)
  161. (defvar *server*)
  162. (defun run-server (&key background
  163. (socket (or (uiop:getenv "LSERVER_SOCKET") *default-socket*))
  164. (home (or (uiop:getenv "LSERVER_HOME") *lserver-home*)))
  165. (setf *server* (make-server :home home :socket-file socket))
  166. (setup-server *server*)
  167. (format t "Starting listening on ~A.~%" (socket-address *server*))
  168. (if background
  169. (bt:make-thread (lambda () (start-server *server*)) :name "lserver main thread")
  170. (start-server *server*)))
  171. (defun lserver-homedir-pathname (&optional (server *server*))
  172. (server-home server))