aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Mathieu Othacehe <othacehe@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (gnu installer dump)
  #:use-module (gnu installer utils)
  #:use-module (guix build utils)
  #:use-module (srfi srfi-11)
  #:use-module (ice-9 iconv)
  #:use-module (ice-9 match)
  #:use-module (ice-9 popen)
  #:use-module (ice-9 textual-ports)
  #:use-module (web client)
  #:use-module (web http)
  #:use-module (web response)
  #:use-module (webutils multipart)
  #:export (%core-dump
            prepare-dump
            make-dump
            send-dump-report))

;; The installer crash dump type.
(define %dump-type "installer-dump")

;; The core dump file.
(define %core-dump "/tmp/installer-core-dump")

(define (result->list result)
  "Return the alist for the given RESULT."
  (hash-map->list (lambda (k v)
                    (cons k v))
                  result))

(define* (prepare-dump key args #:key result)
  "Create a crash dump directory.  KEY and ARGS represent the thrown error.
RESULT is the installer result hash table.  Returns the created directory path."
  (define now (localtime (current-time)))
  (define dump-dir
    (format #f "/tmp/dump.~a"
            (strftime "%F.%H.%M.%S" now)))
  (mkdir-p dump-dir)
  (with-directory-excursion dump-dir
    ;; backtrace
    (call-with-output-file "installer-backtrace"
      (lambda (port)
        (display-backtrace (make-stack #t) port)
        (print-exception port
                         (stack-ref (make-stack #t) 1)
                         key args)))

    ;; installer result
    (call-with-output-file "installer-result"
      (lambda (port)
        (write (result->list result) port)))

    ;; syslog
    (copy-file "/var/log/messages" "syslog")

    ;; core dump
    (when (file-exists? %core-dump)
      (copy-file %core-dump "core-dump"))

    ;; dmesg
    (let ((pipe (open-pipe* OPEN_READ "dmesg")))
      (call-with-output-file "dmesg"
        (lambda (port)
          (dump-port pipe port)
          (close-pipe pipe)))))
  dump-dir)

(define* (make-dump dump-dir file-choices)
  "Create a crash dump archive from DUMP-DIR containing FILE-CHOICES.
Returns the archive path."
  (define output (string-append (basename dump-dir) ".tar.gz"))
  (with-directory-excursion (dirname dump-dir)
    (apply system* "tar" "-zcf" output
           (map (lambda (f)
                  (string-append (basename dump-dir) "/" f))
                file-choices)))
  (canonicalize-path (string-append (dirname dump-dir) "/" output)))

(define* (send-dump-report dump
                           #:key
                           (url "https://dump.guix.gnu.org"))
  "Turn the DUMP archive into a multipart body and send it to the Guix crash
dump server at URL."
  (define (match-boundary kont)
    (match-lambda
      (('boundary . (? string? b))
       (kont b))
      (x #f)))

  (define (response->string response)
    (bytevector->string
     (read-response-body response)
     "UTF-8"))

  (let-values (((body boundary)
                (call-with-input-file dump
                  (lambda (port)
                    (format-multipart-body
                     `((,%dump-type . ,port)))))))
    (false-if-exception
     (response->string
      (http-post
       (string-append url "/upload")
       #:keep-alive? #t
       #:streaming? #t
       #:headers `((content-type
                    . (multipart/form-data
                       (boundary . ,boundary))))
       #:body body)))))
(guix-buffer-message, guix-buffer-history-item, guix-buffer-set) (guix-buffer-display-entries-current) (guix-buffer-get-display-entries-current) (guix-buffer-display-entries, guix-buffer-get-display-entries): New procedures. * emacs/guix-info.el: Adjust for the procedures renaming. (guix-info-define-interface): Add ':show-entries-function' keyword. * emacs/guix-list.el: Likewise. * emacs/guix-ui.el (guix-ui-define-interface): Generate 'guix-ENTRY-TYPE-BUFFER-TYPE-get-entries' procedure based on 'guix-ui-get-entries'. * emacs/guix.el (guix-get-show-packages, guix-get-show-generations): Adjust for the procedures renaming. Alex Kost 2016-01-02emacs: Generalize buffer naming....* emacs/guix-base.el (guix-buffer-name): New procedure. (guix-buffer-define-interface): Make ':buffer-name' a required keyword. (guix-update-after-operation, guix-buffer-name-function) (guix-buffer-name-simple, guix-buffer-name-default, guix-buffer-name) (guix-buffer-p, guix-buffers, guix-update-buffer) (guix-update-buffers-maybe-after-operation): Adjust, move and rename to... * emacs/guix-ui.el (guix-ui-update-after-operation) (guix-ui-buffer-name-function, guix-ui-buffer-name-simple) (guix-ui-buffer-name-default, guix-ui-buffer-name) (guix-ui-buffer?, guix-ui-buffers, guix-ui-update-buffer) (guix-ui-update-buffers-after-operation): ... this. (guix-ui-define-interface): Generate 'guix-ENTRY-TYPE-BUFFER-TYPE-buffer-name' procedure and pass it as ':buffer-name' argument. (guix-ui): New custom group. * emacs/guix-info.el: Specify ':buffer-name' for the defined interfaces. * emacs/guix-list.el: Likewise. * doc/emacs.texi (Emacs Appearance): Adjust accordingly. Alex Kost 2016-01-02emacs: Split 'guix-root-map' keymap....* emacs/guix-base.el: (guix-root-map): Split into... (guix-buffer-map): ... this and... * emacs/guix-ui.el (guix-ui-map): ... this. (guix-ui-define-interface): Generate 'guix-ENTRY-TYPE-BUFFER-TYPE-mode-map' keymaps inheriting them from 'guix-ui-map' and 'guix-BUFFER-TYPE-mode-map'. * emacs/guix-info.el (guix-info-mode-map): Use 'guix-buffer-map'. * emacs/guix-list.el (guix-list-mode-map): Likewise. * doc/emacs.texi (Emacs Keymaps): Document new keymaps. Alex Kost 2016-01-02emacs: Factorize macros for defining interfaces....Make a root 'guix-buffer-define-interface' macro. It should generate a common code for any type of interface. Inherit 'guix-info-define-interface' and 'guix-list-define-interface' from it. They should provide a general 'info'/'list' interface for any data. Finally, make 'guix-ui-define-interface' for the common code for interfaces to Guix packages and generations, and inherit 'guix-ui-info-define-interface' and 'guix-ui-list-define-interface' from it. * emacs/guix-base.el (guix-define-buffer-type): Rename to... (guix-buffer-define-interface): ... this. Rename internal variables ('buf-' -> 'buffer-'). Move ':required' keyword to 'guix-ui-define-interface'. * emacs/guix-info.el (guix-info-define-interface): New macro. (guix-info-font-lock-keywords): New variable. * emacs/guix-list.el (guix-list-define-entry-type): Rename to... (guix-list-define-interface): ... this. (guix-list-font-lock-keywords): New variable. (guix-list-describe-ids): Move and rename to... * emacs/guix-ui.el: New file. (guix-ui-list-describe): ... this. (guix-ui-define-interface, guix-ui-info-define-interface) (guix-ui-list-define-interface): New macros. (guix-ui-font-lock-keywords): New variable. * emacs.am (ELFILES): Add "emacs/guix-ui.el" Alex Kost