1#!/usr/local/bin/python3.8 2 3## Printing troubleshooter 4 5## Copyright (C) 2008, 2010, 2014 Red Hat, Inc. 6## Authors: 7## Tim Waugh <twaugh@redhat.com> 8 9## This program is free software; you can redistribute it and/or modify 10## it under the terms of the GNU General Public License as published by 11## the Free Software Foundation; either version 2 of the License, or 12## (at your option) any later version. 13 14## This program is distributed in the hope that it will be useful, 15## but WITHOUT ANY WARRANTY; without even the implied warranty of 16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17## GNU General Public License for more details. 18 19## You should have received a copy of the GNU General Public License 20## along with this program; if not, write to the Free Software 21## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 23from gi.repository import Gtk 24 25import cups 26import os 27from tempfile import NamedTemporaryFile 28import datetime 29import time 30from timedops import TimedOperation 31from .base import * 32 33try: 34 from systemd import journal 35except: 36 journal = False 37 38class ErrorLogFetch(Question): 39 def __init__ (self, troubleshooter): 40 Question.__init__ (self, troubleshooter, "Error log fetch") 41 page = self.initial_vbox (_("Retrieve Journal Entries"), 42 _("No system journal entries were found. " 43 "This may be because you are not an " 44 "administrator. To fetch journal entries " 45 "please run this command:")) 46 self.entry = Gtk.Entry () 47 self.entry.set_editable (False) 48 page.pack_start (self.entry, False, False, 0) 49 troubleshooter.new_page (page, self) 50 self.persistent_answers = {} 51 52 def display (self): 53 answers = self.troubleshooter.answers 54 parent = self.troubleshooter.get_window () 55 self.answers = {} 56 checkpoint = answers.get ('error_log_checkpoint') 57 cursor = answers.get ('error_log_cursor') 58 timestamp = answers.get ('error_log_timestamp') 59 60 if ('error_log' in self.persistent_answers or 61 'journal' in self.persistent_answers): 62 checkpoint = None 63 cursor = None 64 65 def fetch_log (c): 66 prompt = c._get_prompt_allowed () 67 c._set_prompt_allowed (False) 68 c._connect () 69 with NamedTemporaryFile (delete=False) as tmpf: 70 success = False 71 try: 72 c.getFile ('/admin/log/error_log', file = tmpf) 73 success = True 74 except cups.HTTPError: 75 try: 76 os.remove (tmpf.name) 77 except OSError: 78 pass 79 80 c._set_prompt_allowed (prompt) 81 if success: 82 return tmpf.file 83 84 return None 85 86 now = datetime.datetime.fromtimestamp (time.time ()).strftime ("%F %T") 87 self.authconn = self.troubleshooter.answers['_authenticated_connection'] 88 if 'error_log_debug_logging_set' in answers: 89 try: 90 self.op = TimedOperation (self.authconn.adminGetServerSettings, 91 parent=parent) 92 settings = self.op.run () 93 except cups.IPPError: 94 return False 95 96 settings[cups.CUPS_SERVER_DEBUG_LOGGING] = '0' 97 orig_settings = answers['cups_server_settings'] 98 settings['MaxLogSize'] = orig_settings.get ('MaxLogSize', '2000000') 99 success = False 100 def set_settings (connection, settings): 101 connection.adminSetServerSettings (settings) 102 103 # Now reconnect. 104 attempt = 1 105 while attempt <= 5: 106 try: 107 time.sleep (1) 108 connection._connect () 109 break 110 except RuntimeError: 111 # Connection failed 112 attempt += 1 113 114 try: 115 116 self.op = TimedOperation (set_settings, 117 (self.authconn, settings), 118 parent=parent) 119 self.op.run () 120 self.persistent_answers['error_log_debug_logging_unset'] = True 121 except cups.IPPError: 122 pass 123 124 self.answers = {} 125 if journal and cursor is not None: 126 def journal_format (x): 127 try: 128 priority = "XACEWNIDd"[x['PRIORITY']] 129 except (IndexError, TypeError): 130 priority = " " 131 132 return (priority + " " + 133 x['__REALTIME_TIMESTAMP'].strftime("[%m/%b/%Y:%T]") + 134 " " + x['MESSAGE']) 135 136 r = journal.Reader () 137 r.seek_cursor (cursor) 138 r.add_match (_SYSTEMD_UNIT="cups.service") 139 self.answers['journal'] = [journal_format (x) for x in r] 140 141 if checkpoint is not None: 142 self.op = TimedOperation (fetch_log, 143 (self.authconn,), 144 parent=parent) 145 tmpfname = self.op.run () 146 if tmpfname is not None: 147 f = open (tmpfname) 148 f.seek (checkpoint) 149 lines = f.readlines () 150 os.remove (tmpfname) 151 self.answers = { 'error_log': [x.strip () for x in lines] } 152 153 if (len (self.answers.get ('journal', [])) + 154 len (self.answers.get ('error_log', []))) == 0: 155 cmd = ("su -c 'journalctl -u cups.service " 156 "--since=\"%s\" --until=\"%s\"' > troubleshoot-logs.txt" % 157 (timestamp, now)) 158 self.entry.set_text (cmd) 159 return True 160 161 return False 162 163 def collect_answer (self): 164 answers = self.persistent_answers.copy () 165 answers.update (self.answers) 166 return answers 167 168 def cancel_operation (self): 169 self.op.cancel () 170 171 # Abandon the CUPS connection and make another. 172 answers = self.troubleshooter.answers 173 factory = answers['_authenticated_connection_factory'] 174 self.authconn = factory.get_connection () 175 self.answers['_authenticated_connection'] = self.authconn 176