;;;; OS interface functions for SBCL under BSD Unix.

;;;; This code was written as part of the CMU Common Lisp project at
;;;; Carnegie Mellon University, and has been placed in the public
;;;; domain.

(in-package "SB!IMPL")

;;;; Check that target machine features are set up consistently with
;;;; this file.
#!-bsd
(eval-when (:compile-toplevel :load-toplevel :execute)
  (error "The :BSD feature is missing, we shouldn't be doing this code."))

(define-alien-routine ("sysctl" %sysctl) int
  (name (* int))
  (namelen unsigned-int)
  (oldp (* t))
  (oldlenp (* sb!unix:size-t))
  (newp (* t))
  (newlen sb!unix:size-t))

#!+darwin
(define-alien-routine ("sysctlbyname" %sysctlbyname) int
  (name c-string)
  (oldp (* t))
  (oldlenp (* sb!unix:size-t))
  (newp (* t))
  (newlen sb!unix:size-t))

(defun sysctl (type &rest name)
  #!+sb-doc
  "Retrieves an integer or string value with the given name."
  (let ((name-len (length name)))
    (when (> name-len ctl-maxname)
      (error "sysctl name ~S is too long" name))
    (with-alien ((name-array (array int #.ctl-maxname))
                 (result-len sb!unix:size-t))
      (dotimes (off name-len)
        (setf (deref name-array off) (elt name off)))
      (ecase type
        (:int
         (with-alien ((result int))
           (setf result-len (alien-size int :bytes))
           (unless (minusp (%sysctl (cast name-array (* int)) name-len
                                    (addr result) (addr result-len) nil 0))
             result)))
        (:str
         (unless (minusp (%sysctl (cast name-array (* int)) name-len
                                  nil (addr result-len) nil 0))
           (with-alien ((result (* char) (make-alien char result-len)))
             (if (minusp (%sysctl (cast name-array (* int)) name-len
                                  result (addr result-len) nil 0))
                 (free-alien result)
                 (sb!unix::newcharstar-string result)))))))))

#!+darwin
(defun sysctlbyname (type name)
  #!+sb-doc
  "Retrieves an integer or string value with the given name."
  (with-alien ((result-len sb!unix:size-t))
    (ecase type
      (:int
       (with-alien ((result int))
         (setf result-len (alien-size int :bytes))
         (unless (minusp (%sysctlbyname name (addr result)
                                        (addr result-len) nil 0))
           result)))
      (:str
       (unless (minusp (%sysctlbyname name nil (addr result-len) nil 0))
         (with-alien ((result (* char) (make-alien char result-len)))
           (if (minusp (%sysctlbyname name result (addr result-len) nil 0))
               (free-alien result)
               (sb!unix::newcharstar-string result))))))))

(defun software-type ()
  #!+sb-doc
  "Return a string describing the supporting software."
  (sysctl :str ctl-kern kern-ostype))

(defun software-version ()
  #!+sb-doc
  "Return a string describing version of the supporting software, or NIL
   if not available."
  (or sb!sys::*software-version*
      (setf sb!sys::*software-version*
            (sysctl :str ctl-kern kern-osrelease))))

;;; Return system time, user time and number of page faults.
(defun get-system-info ()
  (multiple-value-bind (err? utime stime maxrss ixrss idrss
                             isrss minflt majflt)
                       (sb!unix:unix-getrusage sb!unix:rusage_self)
    (declare (ignore maxrss ixrss idrss isrss minflt))
    (unless err?
      (simple-perror "Unix system call getrusage() failed" :errno utime))

    (values utime stime majflt)))

;;; Return the system page size.
(defun get-page-size ()
  (sysctl :int ctl-hw hw-pagesize))

;;; support for CL:MACHINE-VERSION defined OAOO elsewhere
(defun get-machine-version ()
  (or #!+darwin (sysctlbyname :str "machdep.cpu.brand_string")
      (sysctl :str ctl-hw hw-model)))
