Back to index

apport  2.3
Functions | Variables
apport.hookutils Namespace Reference

Functions

def path_to_key
def attach_file_if_exists
def read_file
def attach_file
def attach_conffiles
def attach_upstart_overrides
def attach_dmesg
def attach_dmi
def attach_hardware
def attach_alsa
def command_available
def command_output
def _root_command_prefix
def root_command_output
def attach_root_command_outputs
def recent_syslog
def recent_logfile
def xsession_errors
def pci_devices
def usb_devices
def files_in_package
def attach_gconf
def attach_gsettings_schema
def attach_gsettings_package
def attach_network
def attach_wifi
def attach_printing
def attach_mac_events
def attach_related_packages
def package_versions
def shared_libraries
def links_with_shared_library
def _get_module_license
def nonfree_kernel_modules
def __drm_con_info
def attach_drm_info
def in_session_of_problem
def attach_default_grub

Variables

string _path_key_trans = ''
int PCI_MASS_STORAGE = 0x01
int PCI_NETWORK = 0x02
int PCI_DISPLAY = 0x03
int PCI_MULTIMEDIA = 0x04
int PCI_MEMORY = 0x05
int PCI_BRIDGE = 0x06
int PCI_SIMPLE_COMMUNICATIONS = 0x07
int PCI_BASE_SYSTEM_PERIPHERALS = 0x08
int PCI_INPUT_DEVICES = 0x09
int PCI_DOCKING_STATIONS = 0x0a
int PCI_PROCESSORS = 0x0b
int PCI_SERIAL_BUS = 0x0c

Detailed Description

Convenience functions for use in package hooks.

Function Documentation

def apport.hookutils.__drm_con_info (   con) [private]

Definition at line 812 of file hookutils.py.

00812 
00813 def __drm_con_info(con):
00814     info = ''
00815     for f in os.listdir(con):
00816         path = os.path.join(con, f)
00817         if f == 'uevent' or not os.path.isfile(path):
00818             continue
00819         val = open(path).read().strip()
00820         # format some well-known attributes specially
00821         if f == 'modes':
00822             val = val.replace('\n', ' ')
00823         if f == 'edid':
00824             val = base64.b64encode(val)
00825             f += '-base64'
00826         info += '%s: %s\n' % (f, val)
00827     return info
00828 

Here is the caller graph for this function:

def apport.hookutils._get_module_license (   module) [private]
Return the license for a given kernel module.

Definition at line 773 of file hookutils.py.

00773 
00774 def _get_module_license(module):
00775     '''Return the license for a given kernel module.'''
00776 
00777     try:
00778         modinfo = subprocess.Popen(['/sbin/modinfo', module],
00779                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00780         out = modinfo.communicate()[0].decode('UTF-8')
00781         if modinfo.returncode != 0:
00782             return 'invalid'
00783     except OSError:
00784         return None
00785     for l in out.splitlines():
00786         fields = l.split(':', 1)
00787         if len(fields) < 2:
00788             continue
00789         if fields[0] == 'license':
00790             return fields[1].strip()
00791 
00792     return None
00793 

Here is the caller graph for this function:

Definition at line 369 of file hookutils.py.

00369 
00370 def _root_command_prefix():
00371     if os.getuid() == 0:
00372         prefix = []
00373     elif os.getenv('DISPLAY') and \
00374             subprocess.call(['which', 'kdesudo'], stdout=subprocess.PIPE,
00375                             stderr=subprocess.PIPE) == 0 and \
00376             subprocess.call(['pgrep', '-x', '-u', str(os.getuid()), 'ksmserver'],
00377                             stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
00378         prefix = ['kdesudo', '--desktop', '/usr/share/applications/apport-kde-mime.desktop',
00379                   '--', 'env', '-u', 'LANGUAGE', 'LC_MESSAGES=C']
00380     elif os.getenv('DISPLAY') and \
00381             subprocess.call(['which', 'gksu'], stdout=subprocess.PIPE,
00382                             stderr=subprocess.PIPE) == 0 and \
00383             subprocess.call(['pgrep', '-x', '-u', str(os.getuid()), 'gnome-panel|gconfd-2'],
00384                             stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0:
00385         prefix = ['gksu', '-D', 'Apport', '--', 'env', '-u', 'LANGUAGE', 'LC_MESSAGES=C']
00386     else:
00387         prefix = ['sudo', 'LC_MESSAGES=C', 'LANGUAGE=']
00388 
00389     return prefix
00390 

Here is the caller graph for this function:

def apport.hookutils.attach_alsa (   report)
Attach ALSA subsystem information to the report.

(loosely based on http://www.alsa-project.org/alsa-info.sh)

Definition at line 264 of file hookutils.py.

00264 
00265 def attach_alsa(report):
00266     '''Attach ALSA subsystem information to the report.
00267 
00268     (loosely based on http://www.alsa-project.org/alsa-info.sh)
00269     '''
00270     attach_file_if_exists(report, os.path.expanduser('~/.asoundrc'),
00271                           'UserAsoundrc')
00272     attach_file_if_exists(report, os.path.expanduser('~/.asoundrc.asoundconf'),
00273                           'UserAsoundrcAsoundconf')
00274     attach_file_if_exists(report, '/etc/asound.conf')
00275     attach_file_if_exists(report, '/proc/asound/version', 'AlsaVersion')
00276     attach_file(report, '/proc/cpuinfo', 'ProcCpuinfo')
00277 
00278     report['AlsaDevices'] = command_output(['ls', '-l', '/dev/snd/'])
00279     report['AplayDevices'] = command_output(['aplay', '-l'])
00280     report['ArecordDevices'] = command_output(['arecord', '-l'])
00281 
00282     report['PciMultimedia'] = pci_devices(PCI_MULTIMEDIA)
00283 
00284     cards = []
00285     if os.path.exists('/proc/asound/cards'):
00286         with open('/proc/asound/cards') as fd:
00287             for line in fd:
00288                 if ']:' in line:
00289                     fields = line.lstrip().split()
00290                     cards.append(int(fields[0]))
00291 
00292     for card in cards:
00293         key = 'Card%d.Amixer.info' % card
00294         report[key] = command_output(['amixer', '-c', str(card), 'info'])
00295         key = 'Card%d.Amixer.values' % card
00296         report[key] = command_output(['amixer', '-c', str(card)])
00297 
00298         for codecpath in glob.glob('/proc/asound/card%d/codec*' % card):
00299             if os.path.isfile(codecpath):
00300                 codec = os.path.basename(codecpath)
00301                 key = 'Card%d.Codecs.%s' % (card, path_to_key(codec))
00302                 attach_file(report, codecpath, key=key)
00303             elif os.path.isdir(codecpath):
00304                 codec = os.path.basename(codecpath)
00305                 for name in os.listdir(codecpath):
00306                     path = os.path.join(codecpath, name)
00307                     key = 'Card%d.Codecs.%s.%s' % (card, path_to_key(codec), path_to_key(name))
00308                     attach_file(report, path, key)
00309 
00310     report['AudioDevicesInUse'] = command_output(
00311         ['fuser', '-v'] + glob.glob('/dev/dsp*') + glob.glob('/dev/snd/*') + glob.glob('/dev/seq*'))
00312 
00313     if os.path.exists('/usr/bin/pacmd'):
00314         report['PulseList'] = command_output(['pacmd', 'list'])
00315 
00316     attach_dmi(report)
00317     attach_dmesg(report)
00318 
00319     # This seems redundant with the amixer info, do we need it?
00320     #report['AlsactlStore'] = command-output(['alsactl', '-f', '-', 'store'])
00321 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_conffiles (   report,
  package,
  conffiles = None,
  ui = None 
)
Attach information about any modified or deleted conffiles.

If conffiles is given, only this subset will be attached. If ui is given,
ask whether the contents of the file may be added to the report; if this is
denied, or there is no UI, just mark it as "modified" in the report.

Definition at line 120 of file hookutils.py.

00120 
00121 def attach_conffiles(report, package, conffiles=None, ui=None):
00122     '''Attach information about any modified or deleted conffiles.
00123 
00124     If conffiles is given, only this subset will be attached. If ui is given,
00125     ask whether the contents of the file may be added to the report; if this is
00126     denied, or there is no UI, just mark it as "modified" in the report.
00127     '''
00128     modified = packaging.get_modified_conffiles(package)
00129 
00130     for path, contents in modified.items():
00131         if conffiles and path not in conffiles:
00132             continue
00133 
00134         key = 'modified.conffile.' + path_to_key(path)
00135         if contents == '[deleted]':
00136             report[key] = contents
00137             continue
00138 
00139         if ui:
00140             response = ui.yesno('It seems you have modified the contents of "%s".  Would you like to add the contents of it to your bug report?' % path)
00141             if response:
00142                 report[key] = contents
00143             else:
00144                 report[key] = '[modified]'
00145         else:
00146             report[key] = '[modified]'
00147 
00148         mtime = datetime.datetime.fromtimestamp(os.stat(path).st_mtime)
00149         report['mtime.conffile.' + path_to_key(path)] = mtime.isoformat()
00150 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_default_grub (   report,
  key = None 
)
attach /etc/default/grub after filtering out password lines

Definition at line 893 of file hookutils.py.

00893 
00894 def attach_default_grub(report, key=None):
00895     '''attach /etc/default/grub after filtering out password lines'''
00896 
00897     path = '/etc/default/grub'
00898     if not key:
00899         key = path_to_key(path)
00900 
00901     if os.path.exists(path):
00902         with open(path, 'r') as f:
00903             filtered = [l if not l.startswith('password')
00904                         else '### PASSWORD LINE REMOVED ###'
00905                         for l in f.readlines()]
00906             report[key] = ''.join(filtered)

Here is the call graph for this function:

Here is the caller graph for this function:

Attach information from the kernel ring buffer (dmesg).

This will not overwrite already existing information.

Definition at line 166 of file hookutils.py.

00166 
00167 def attach_dmesg(report):
00168     '''Attach information from the kernel ring buffer (dmesg).
00169 
00170     This will not overwrite already existing information.
00171     '''
00172     try:
00173         if not report.get('BootDmesg', '').strip():
00174             with open('/var/log/dmesg') as f:
00175                 report['BootDmesg'] = f.read()
00176     except IOError:
00177         pass
00178     if not report.get('CurrentDmesg', '').strip():
00179         dmesg = command_output(['sh', '-c', 'dmesg | comm -13 --nocheck-order /var/log/dmesg -'])
00180         # if an initial message was truncated by the ring buffer, skip over it
00181         first_newline = dmesg.find('\n[')
00182         if first_newline != -1:
00183             dmesg = dmesg[first_newline + 1:]
00184         report['CurrentDmesg'] = dmesg
00185 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_dmi (   report)

Definition at line 186 of file hookutils.py.

00186 
00187 def attach_dmi(report):
00188     dmi_dir = '/sys/class/dmi/id'
00189     if os.path.isdir(dmi_dir):
00190         for f in os.listdir(dmi_dir):
00191             p = '%s/%s' % (dmi_dir, f)
00192             st = os.stat(p)
00193             # ignore the root-only ones, since they have serial numbers
00194             if not stat.S_ISREG(st.st_mode) or (st.st_mode & 4 == 0):
00195                 continue
00196             if f in ('subsystem', 'uevent'):
00197                 continue
00198 
00199             try:
00200                 with open(p) as fd:
00201                     value = fd.read().strip()
00202             except (OSError, IOError):
00203                 continue
00204             if value:
00205                 report['dmi.' + f.replace('_', '.')] = value
00206 

Here is the caller graph for this function:

Add information about DRM hardware.

Collect information from /sys/class/drm/.

Definition at line 829 of file hookutils.py.

00829 
00830 def attach_drm_info(report):
00831     '''Add information about DRM hardware.
00832 
00833     Collect information from /sys/class/drm/.
00834     '''
00835     drm_dir = '/sys/class/drm'
00836     if not os.path.isdir(drm_dir):
00837         return
00838     for f in os.listdir(drm_dir):
00839         con = os.path.join(drm_dir, f)
00840         if os.path.exists(os.path.join(con, 'enabled')):
00841             # DRM can set an arbitrary string for its connector paths.
00842             report['DRM.' + path_to_key(f)] = __drm_con_info(con)
00843 

Here is the call graph for this function:

def apport.hookutils.attach_file (   report,
  path,
  key = None,
  overwrite = True,
  force_unicode = False 
)
Attach a file to the report.

If key is not specified, the key name will be derived from the file
name with path_to_key().

If overwrite is True, an existing key will be updated. If it is False, a
new key with '_' appended will be added instead.

If the contents is valid UTF-8, or force_unicode is True, then the value
will a string, otherwise it will be bytes.

Definition at line 98 of file hookutils.py.

00098 
00099 def attach_file(report, path, key=None, overwrite=True, force_unicode=False):
00100     '''Attach a file to the report.
00101 
00102     If key is not specified, the key name will be derived from the file
00103     name with path_to_key().
00104 
00105     If overwrite is True, an existing key will be updated. If it is False, a
00106     new key with '_' appended will be added instead.
00107 
00108     If the contents is valid UTF-8, or force_unicode is True, then the value
00109     will a string, otherwise it will be bytes.
00110     '''
00111     if not key:
00112         key = path_to_key(path)
00113 
00114     # Do not clobber existing keys
00115     if not overwrite:
00116         while key in report:
00117             key += '_'
00118     report[key] = read_file(path, force_unicode=force_unicode)
00119 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_file_if_exists (   report,
  path,
  key = None,
  overwrite = True,
  force_unicode = False 
)
Attach file contents if file exists.

If key is not specified, the key name will be derived from the file
name with path_to_key().

If overwrite is True, an existing key will be updated. If it is False, a
new key with '_' appended will be added instead.

If the contents is valid UTF-8, or force_unicode is True, then the value
will a string, otherwise it will be bytes.

Definition at line 57 of file hookutils.py.

00057 
00058 def attach_file_if_exists(report, path, key=None, overwrite=True, force_unicode=False):
00059     '''Attach file contents if file exists.
00060 
00061     If key is not specified, the key name will be derived from the file
00062     name with path_to_key().
00063 
00064     If overwrite is True, an existing key will be updated. If it is False, a
00065     new key with '_' appended will be added instead.
00066 
00067     If the contents is valid UTF-8, or force_unicode is True, then the value
00068     will a string, otherwise it will be bytes.
00069     '''
00070     if not key:
00071         key = path_to_key(path)
00072 
00073     if os.path.exists(path):
00074         attach_file(report, path, key, overwrite, force_unicode)
00075 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_gconf (   report,
  package 
)
Obsolete

Definition at line 568 of file hookutils.py.

00568 
00569 def attach_gconf(report, package):
00570     '''Obsolete'''
00571 
00572     # keeping a no-op function for some time to not break hooks
00573     pass
00574 

def apport.hookutils.attach_gsettings_package (   report,
  package 
)
Attach user-modified gsettings keys of all schemas in a package.

Definition at line 608 of file hookutils.py.

00608 
00609 def attach_gsettings_package(report, package):
00610     '''Attach user-modified gsettings keys of all schemas in a package.'''
00611 
00612     for schema_file in files_in_package(package, '/usr/share/glib-2.0/schemas/*.gschema.xml'):
00613         schema = os.path.basename(schema_file)[:-12]
00614         attach_gsettings_schema(report, schema)
00615 

Here is the call graph for this function:

def apport.hookutils.attach_gsettings_schema (   report,
  schema 
)
Attach user-modified gsttings keys of a schema.

Definition at line 575 of file hookutils.py.

00575 
00576 def attach_gsettings_schema(report, schema):
00577     '''Attach user-modified gsttings keys of a schema.'''
00578 
00579     cur_value = report.get('GsettingsChanges', '')
00580 
00581     defaults = {}  # schema -> key ->  value
00582     env = os.environ.copy()
00583     env['XDG_CONFIG_HOME'] = '/nonexisting'
00584     gsettings = subprocess.Popen(['gsettings', 'list-recursively', schema],
00585                                  env=env, stdout=subprocess.PIPE)
00586     for l in gsettings.stdout:
00587         try:
00588             (schema, key, value) = l.split(None, 2)
00589             value = value.rstrip()
00590         except ValueError:
00591             continue  # invalid line
00592         defaults.setdefault(schema, {})[key] = value
00593 
00594     gsettings = subprocess.Popen(['gsettings', 'list-recursively', schema],
00595                                  stdout=subprocess.PIPE)
00596     for l in gsettings.stdout:
00597         try:
00598             (schema, key, value) = l.split(None, 2)
00599             value = value.rstrip()
00600         except ValueError:
00601             continue  # invalid line
00602 
00603         if value != defaults.get(schema, {}).get(key, ''):
00604             cur_value += '%s %s %s\n' % (schema, key, value)
00605 
00606     report['GsettingsChanges'] = cur_value
00607 

Here is the caller graph for this function:

Attach a standard set of hardware-related data to the report, including:

- kernel dmesg (boot and current)
- /proc/interrupts
- /proc/cpuinfo
- /proc/cmdline
- /proc/modules
- lspci -vvnn
- lsusb
- devices from udev
- DMI information from /sys
- prtconf (sparc)
- pccardctl status/ident

Definition at line 207 of file hookutils.py.

00207 
00208 def attach_hardware(report):
00209     '''Attach a standard set of hardware-related data to the report, including:
00210 
00211     - kernel dmesg (boot and current)
00212     - /proc/interrupts
00213     - /proc/cpuinfo
00214     - /proc/cmdline
00215     - /proc/modules
00216     - lspci -vvnn
00217     - lsusb
00218     - devices from udev
00219     - DMI information from /sys
00220     - prtconf (sparc)
00221     - pccardctl status/ident
00222     '''
00223     attach_dmesg(report)
00224 
00225     attach_file(report, '/proc/interrupts', 'ProcInterrupts')
00226     attach_file(report, '/proc/cpuinfo', 'ProcCpuinfo')
00227     attach_file(report, '/proc/cmdline', 'ProcKernelCmdLine')
00228     attach_file(report, '/var/log/udev', 'UdevLog', force_unicode=True)
00229 
00230     if os.path.exists('/sys/bus/pci'):
00231         report['Lspci'] = command_output(['lspci', '-vvnn'])
00232     report['Lsusb'] = command_output(['lsusb'])
00233     report['ProcModules'] = command_output(['sort', '/proc/modules'])
00234     report['UdevDb'] = command_output(['udevadm', 'info', '--export-db'])
00235 
00236     # anonymize partition labels
00237     l = report['UdevLog']
00238     l = re.sub('ID_FS_LABEL=(.*)', 'ID_FS_LABEL=<hidden>', l)
00239     l = re.sub('ID_FS_LABEL_ENC=(.*)', 'ID_FS_LABEL_ENC=<hidden>', l)
00240     l = re.sub('by-label/(.*)', 'by-label/<hidden>', l)
00241     l = re.sub('ID_FS_LABEL=(.*)', 'ID_FS_LABEL=<hidden>', l)
00242     l = re.sub('ID_FS_LABEL_ENC=(.*)', 'ID_FS_LABEL_ENC=<hidden>', l)
00243     l = re.sub('by-label/(.*)', 'by-label/<hidden>', l)
00244     report['UdevLog'] = l
00245 
00246     attach_dmi(report)
00247 
00248     # Use the hardware information to create a machine type.
00249     if 'dmi.sys.vendor' in report and 'dmi.product.name' in report:
00250         report['MachineType'] = '%s %s' % (report['dmi.sys.vendor'],
00251                                            report['dmi.product.name'])
00252 
00253     if command_available('prtconf'):
00254         report['Prtconf'] = command_output(['prtconf'])
00255 
00256     if command_available('pccardctl'):
00257         out = command_output(['pccardctl', 'status']).strip()
00258         if out:
00259             report['PccardctlStatus'] = out
00260         out = command_output(['pccardctl', 'ident']).strip()
00261         if out:
00262             report['PccardctlIdent'] = out
00263 

Here is the call graph for this function:

Here is the caller graph for this function:

Attach MAC information and events to the report.

Definition at line 671 of file hookutils.py.

00671 
00672 def attach_mac_events(report):
00673     '''Attach MAC information and events to the report.'''
00674 
00675     mac_regex = 'audit\(|apparmor|selinux|security'
00676     mac_re = re.compile(mac_regex, re.IGNORECASE)
00677     aa_denied_regex = 'apparmor="DENIED"'
00678     aa_denied_re = re.compile(aa_denied_regex, re.IGNORECASE)
00679 
00680     if os.path.exists('/var/log/kern.log'):
00681         report['KernLog'] = recent_logfile('/var/log/kern.log', mac_re)
00682     elif os.path.exists('/var/log/messages'):
00683         report['KernLog'] = recent_logfile('/var/log/messages', mac_re)
00684 
00685     if os.path.exists('/var/run/auditd.pid'):
00686         attach_root_command_outputs(report, {'AuditLog': 'egrep "' + mac_regex + '" /var/log/audit/audit.log'})
00687 
00688     attach_file(report, '/proc/version_signature', 'ProcVersionSignature')
00689     attach_file(report, '/proc/cmdline', 'ProcCmdline')
00690 
00691     if re.search(aa_denied_re, report.get('KernLog', '')) or re.search(aa_denied_re, report.get('AuditLog', '')):
00692         tags = report.get('Tags', '')
00693         if tags:
00694             tags += ' '
00695         report['Tags'] = tags + 'apparmor'
00696 

Here is the call graph for this function:

Attach generic network-related information to report.

Definition at line 616 of file hookutils.py.

00616 
00617 def attach_network(report):
00618     '''Attach generic network-related information to report.'''
00619 
00620     report['IpRoute'] = command_output(['ip', 'route'])
00621     report['IpAddr'] = command_output(['ip', 'addr'])
00622     report['PciNetwork'] = pci_devices(PCI_NETWORK)
00623     attach_file_if_exists(report, '/etc/network/interfaces', key='IfupdownConfig')
00624 
00625     for var in ('http_proxy', 'ftp_proxy', 'no_proxy'):
00626         if var in os.environ:
00627             report[var] = os.environ[var]
00628 

Here is the call graph for this function:

Here is the caller graph for this function:

Attach printing information to the report.

Based on http://wiki.ubuntu.com/PrintingBugInfoScript.

Definition at line 644 of file hookutils.py.

00644 
00645 def attach_printing(report):
00646     '''Attach printing information to the report.
00647 
00648     Based on http://wiki.ubuntu.com/PrintingBugInfoScript.
00649     '''
00650     attach_file_if_exists(report, '/etc/papersize', 'Papersize')
00651     attach_file_if_exists(report, '/var/log/cups/error_log', 'CupsErrorLog')
00652     report['Locale'] = command_output(['locale'])
00653     report['Lpstat'] = command_output(['lpstat', '-v'])
00654 
00655     ppds = glob.glob('/etc/cups/ppd/*.ppd')
00656     if ppds:
00657         nicknames = command_output(['fgrep', '-H', '*NickName'] + ppds)
00658         report['PpdFiles'] = re.sub('/etc/cups/ppd/(.*).ppd:\*NickName: *"(.*)"', '\g<1>: \g<2>', nicknames)
00659 
00660     report['PrintingPackages'] = package_versions(
00661         'foo2zjs', 'foomatic-db', 'foomatic-db-engine',
00662         'foomatic-db-gutenprint', 'foomatic-db-hpijs', 'foomatic-filters',
00663         'foomatic-gui', 'hpijs', 'hplip', 'm2300w', 'min12xxw', 'c2050',
00664         'hpoj', 'pxljr', 'pnm2ppa', 'splix', 'hp-ppd', 'hpijs-ppds',
00665         'linuxprinting.org-ppds', 'openprinting-ppds',
00666         'openprinting-ppds-extra', 'ghostscript', 'cups',
00667         'cups-driver-gutenprint', 'foomatic-db-gutenprint', 'ijsgutenprint',
00668         'cupsys-driver-gutenprint', 'gimp-gutenprint', 'gutenprint-doc',
00669         'gutenprint-locales', 'system-config-printer-common', 'kdeprint')
00670 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_related_packages (   report,
  packages 
)
Attach version information for related packages

In the future, this might also run their hooks.

Definition at line 697 of file hookutils.py.

00697 
00698 def attach_related_packages(report, packages):
00699     '''Attach version information for related packages
00700 
00701     In the future, this might also run their hooks.
00702     '''
00703     report['RelatedPackageVersions'] = package_versions(*packages)
00704 

Here is the call graph for this function:

def apport.hookutils.attach_root_command_outputs (   report,
  command_map 
)
Execute multiple commands as root and put their outputs into report.

command_map is a keyname -> 'shell command' dictionary with the commands to
run. They are all run through /bin/sh, so you need to take care of shell
escaping yourself. To include stderr output of a command, end it with
"2>&1".

Just like root_command_output() this will use gksu, kdesudo, or sudo for
gaining root privileges, depending on the running desktop environment.

This is preferrable to using root_command_output() multiple times, as that
will ask for the password every time.

Definition at line 407 of file hookutils.py.

00407 
00408 def attach_root_command_outputs(report, command_map):
00409     '''Execute multiple commands as root and put their outputs into report.
00410 
00411     command_map is a keyname -> 'shell command' dictionary with the commands to
00412     run. They are all run through /bin/sh, so you need to take care of shell
00413     escaping yourself. To include stderr output of a command, end it with
00414     "2>&1".
00415 
00416     Just like root_command_output() this will use gksu, kdesudo, or sudo for
00417     gaining root privileges, depending on the running desktop environment.
00418 
00419     This is preferrable to using root_command_output() multiple times, as that
00420     will ask for the password every time.
00421     '''
00422     workdir = tempfile.mkdtemp()
00423     try:
00424         # create a shell script with all the commands
00425         script_path = os.path.join(workdir, ':script:')
00426         script = open(script_path, 'w')
00427         for keyname, command in command_map.items():
00428             assert hasattr(command, 'strip'), 'command must be a string (shell command)'
00429             # use "| cat" here, so that we can end commands with 2>&1
00430             # (otherwise it would have the wrong redirection order)
00431             script.write('%s | cat > %s\n' % (command, os.path.join(workdir, keyname)))
00432         script.close()
00433 
00434         # run script
00435         sp = subprocess.Popen(_root_command_prefix() + ['/bin/sh', script_path])
00436         sp.wait()
00437 
00438         # now read back the individual outputs
00439         for keyname in command_map:
00440             f = open(os.path.join(workdir, keyname))
00441             buf = f.read().strip()
00442             if buf:
00443                 report[keyname] = buf
00444             f.close()
00445     finally:
00446         shutil.rmtree(workdir)
00447 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_upstart_overrides (   report,
  package 
)
Attach information about any Upstart override files

Definition at line 151 of file hookutils.py.

00151 
00152 def attach_upstart_overrides(report, package):
00153     '''Attach information about any Upstart override files'''
00154 
00155     try:
00156         files = apport.packaging.get_files(package)
00157     except ValueError:
00158         return
00159 
00160     for file in files:
00161         if os.path.exists(file) and file.startswith('/etc/init/'):
00162             override = file.replace('.conf', '.override')
00163             key = 'upstart.' + override.replace('/etc/init/', '')
00164             attach_file_if_exists(report, override, key)
00165 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.attach_wifi (   report)
Attach wireless (WiFi) network information to report.

Definition at line 629 of file hookutils.py.

00629 
00630 def attach_wifi(report):
00631     '''Attach wireless (WiFi) network information to report.'''
00632 
00633     report['WifiSyslog'] = recent_syslog(re.compile(r'(NetworkManager|modem-manager|dhclient|kernel|wpa_supplicant)(\[\d+\])?:'))
00634     report['IwConfig'] = re.sub(
00635         'ESSID:(.*)', 'ESSID:<hidden>',
00636         re.sub('Encryption key:(.*)', 'Encryption key: <hidden>',
00637                re.sub('Access Point: (.*)', 'Access Point: <hidden>',
00638                       command_output(['iwconfig']))))
00639     report['RfKill'] = command_output(['rfkill', 'list'])
00640     report['CRDA'] = command_output(['iw', 'reg', 'get'])
00641 
00642     attach_file_if_exists(report, '/var/log/wpa_supplicant.log', key='WpaSupplicantLog')
00643 

Here is the call graph for this function:

Here is the caller graph for this function:

Is given command on the executable search path?

Definition at line 322 of file hookutils.py.

00322 
00323 def command_available(command):
00324     '''Is given command on the executable search path?'''
00325     if 'PATH' not in os.environ:
00326         return False
00327     path = os.environ['PATH']
00328     for element in path.split(os.pathsep):
00329         if not element:
00330             continue
00331         filename = os.path.join(element, command)
00332         if os.path.isfile(filename) and os.access(filename, os.X_OK):
00333             return True
00334     return False
00335 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.command_output (   command,
  input = None,
  stderr = subprocess.STDOUT,
  keep_locale = False,
  decode_utf8 = True 
)
Try to execute given command (array) and return its stdout.

In case of failure, a textual error gets returned. This function forces
LC_MESSAGES to C, to avoid translated output in bug reports.

If decode_utf8 is True (default), the output will be converted to a string,
otherwise left as bytes.

Definition at line 337 of file hookutils.py.

00337 
00338                    keep_locale=False, decode_utf8=True):
00339     '''Try to execute given command (array) and return its stdout.
00340 
00341     In case of failure, a textual error gets returned. This function forces
00342     LC_MESSAGES to C, to avoid translated output in bug reports.
00343 
00344     If decode_utf8 is True (default), the output will be converted to a string,
00345     otherwise left as bytes.
00346     '''
00347     env = os.environ.copy()
00348     if not keep_locale:
00349         env['LC_MESSAGES'] = 'C'
00350     try:
00351         sp = subprocess.Popen(command, stdout=subprocess.PIPE,
00352                               stderr=stderr,
00353                               stdin=(input and subprocess.PIPE or None),
00354                               env=env)
00355     except OSError as e:
00356         return 'Error: ' + str(e)
00357 
00358     out = sp.communicate(input)[0]
00359     if sp.returncode == 0:
00360         res = out.strip()
00361     else:
00362         res = (b'Error: command ' + str(command).encode() + b' failed with exit code '
00363                + str(sp.returncode).encode() + b': ' + out)
00364 
00365     if decode_utf8:
00366         res = res.decode('UTF-8', errors='replace')
00367     return res
00368 

Here is the caller graph for this function:

def apport.hookutils.files_in_package (   package,
  globpat = None 
)
Retrieve a list of files owned by package, optionally matching globpat

Definition at line 557 of file hookutils.py.

00557 
00558 def files_in_package(package, globpat=None):
00559     '''Retrieve a list of files owned by package, optionally matching globpat'''
00560 
00561     files = packaging.get_files(package)
00562     if globpat:
00563         result = [f for f in files if glob.fnmatch.fnmatch(f, globpat)]
00564     else:
00565         result = files
00566     return result
00567 

Here is the caller graph for this function:

Check if the problem happened in the currently running XDG session.

This can be used to determine if e. g. ~/.xsession-errors is relevant and
should be attached.

Return None if this cannot be determined due to not being able to talk to
ConsoleKit.

Definition at line 844 of file hookutils.py.

00844 
00845 def in_session_of_problem(report):
00846     '''Check if the problem happened in the currently running XDG session.
00847 
00848     This can be used to determine if e. g. ~/.xsession-errors is relevant and
00849     should be attached.
00850 
00851     Return None if this cannot be determined due to not being able to talk to
00852     ConsoleKit.
00853     '''
00854     # report time is in local TZ
00855     orig_ctime = locale.getlocale(locale.LC_TIME)
00856     try:
00857         locale.setlocale(locale.LC_TIME, 'C')
00858         report_time = time.mktime(time.strptime(report['Date']))
00859     except KeyError:
00860         return None
00861     finally:
00862         locale.setlocale(locale.LC_TIME, orig_ctime)
00863 
00864     try:
00865         bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
00866         ck_manager = Gio.DBusProxy.new_sync(
00867             bus, Gio.DBusProxyFlags.NONE, None,
00868             'org.freedesktop.ConsoleKit', '/org/freedesktop/ConsoleKit/Manager',
00869             'org.freedesktop.ConsoleKit.Manager', None)
00870 
00871         cur_session = ck_manager.GetCurrentSession()
00872 
00873         ck_session = Gio.DBusProxy.new_sync(
00874             bus, Gio.DBusProxyFlags.NONE, None,
00875             'org.freedesktop.ConsoleKit', cur_session,
00876             'org.freedesktop.ConsoleKit.Session', None)
00877 
00878         session_start_time = ck_session.GetCreationTime()
00879     except GLib.GError as e:
00880         sys.stderr.write('Error connecting to ConsoleKit: %s\n' % str(e))
00881         return None
00882 
00883     m = re.match('(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d)(?:\.\d+Z)$', session_start_time)
00884     if m:
00885         # CK gives UTC time
00886         session_start_time = calendar.timegm(time.strptime(m.group(1), '%Y-%m-%dT%H:%M:%S'))
00887     else:
00888         sys.stderr.write('cannot parse time returned by CK: %s\n' % session_start_time)
00889         return None
00890 
00891     return session_start_time <= report_time
00892 

Here is the caller graph for this function:

def apport.hookutils.links_with_shared_library (   path,
  lib 
)
Returns True if the binary at path links with the library named lib.

path should be a fully qualified path (e.g. report['ExecutablePath'])
lib may be of the form 'lib<name>' or 'lib<name>.so.<version>'

Definition at line 754 of file hookutils.py.

00754 
00755 def links_with_shared_library(path, lib):
00756     '''Returns True if the binary at path links with the library named lib.
00757 
00758     path should be a fully qualified path (e.g. report['ExecutablePath'])
00759     lib may be of the form 'lib<name>' or 'lib<name>.so.<version>'
00760     '''
00761 
00762     libs = shared_libraries(path)
00763 
00764     if lib in libs:
00765         return True
00766 
00767     for linked_lib in libs:
00768         if linked_lib.startswith(lib + '.so.'):
00769             return True
00770 
00771     return False
00772 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.nonfree_kernel_modules (   module_list = '/proc/modules')
Check loaded modules and return a list of those which are not free.

Definition at line 794 of file hookutils.py.

00794 
00795 def nonfree_kernel_modules(module_list='/proc/modules'):
00796     '''Check loaded modules and return a list of those which are not free.'''
00797 
00798     try:
00799         with open(module_list) as f:
00800             mods = [l.split()[0] for l in f]
00801     except IOError:
00802         return []
00803 
00804     nonfree = []
00805     for m in mods:
00806         l = _get_module_license(m)
00807         if l and not ('GPL' in l or 'BSD' in l or 'MPL' in l or 'MIT' in l):
00808             nonfree.append(m)
00809 
00810     return nonfree
00811 

Here is the call graph for this function:

Here is the caller graph for this function:

Return a text listing of package names and versions.

Arguments may be package names or globs, e. g. "foo*"

Definition at line 705 of file hookutils.py.

00705 
00706 def package_versions(*packages):
00707     '''Return a text listing of package names and versions.
00708 
00709     Arguments may be package names or globs, e. g. "foo*"
00710     '''
00711     versions = []
00712     for package_pattern in packages:
00713         if not package_pattern:
00714             continue
00715 
00716         matching_packages = packaging.package_name_glob(package_pattern)
00717 
00718         if not matching_packages:
00719             versions.append((package_pattern, 'N/A'))
00720 
00721         for package in sorted(matching_packages):
00722             try:
00723                 version = packaging.get_version(package)
00724             except ValueError:
00725                 version = 'N/A'
00726             if version is None:
00727                 version = 'N/A'
00728             versions.append((package, version))
00729 
00730     package_width, version_width = \
00731         map(max, [map(len, t) for t in zip(*versions)])
00732 
00733     fmt = '%%-%ds %%s' % package_width
00734     return '\n'.join([fmt % v for v in versions])
00735 

Here is the caller graph for this function:

Generate a valid report key name from a file path.

This will replace invalid punctuation symbols with valid ones.

Definition at line 43 of file hookutils.py.

00043 
00044 def path_to_key(path):
00045     '''Generate a valid report key name from a file path.
00046 
00047     This will replace invalid punctuation symbols with valid ones.
00048     '''
00049     if sys.version[0] >= '3':
00050         if type(path) == type(b''):
00051             path = path.decode('UTF-8')
00052     else:
00053         if type(path) != type(b''):
00054             path = path.encode('UTF-8')
00055     return path.translate(_path_key_trans)
00056 

Here is the caller graph for this function:

def apport.hookutils.pci_devices (   pci_classes)
Return a text dump of PCI devices attached to the system.

Definition at line 517 of file hookutils.py.

00517 
00518 def pci_devices(*pci_classes):
00519     '''Return a text dump of PCI devices attached to the system.'''
00520 
00521     if not pci_classes:
00522         return command_output(['lspci', '-vvnn'])
00523 
00524     result = ''
00525     output = command_output(['lspci', '-vvmmnn'])
00526     for paragraph in output.split('\n\n'):
00527         pci_class = None
00528         slot = None
00529 
00530         for line in paragraph.split('\n'):
00531             try:
00532                 key, value = line.split(':', 1)
00533             except ValueError:
00534                 continue
00535             value = value.strip()
00536             key = key.strip()
00537             if key == 'Class':
00538                 n = int(value[-5:-1], 16)
00539                 pci_class = (n & 0xff00) >> 8
00540             elif key == 'Slot':
00541                 slot = value
00542 
00543         if pci_class and slot and pci_class in pci_classes:
00544             if result:
00545                 result += '\n\n'
00546             result += command_output(['lspci', '-vvnns', slot]).strip()
00547 
00548     return result
00549 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.read_file (   path,
  force_unicode = False 
)
Return the contents of the specified path.

If the contents is valid UTF-8, or force_unicode is True, then the value
will a string, otherwise it will be bytes.

Upon error, this will deliver a text representation of the error,
instead of failing.

Definition at line 76 of file hookutils.py.

00076 
00077 def read_file(path, force_unicode=False):
00078     '''Return the contents of the specified path.
00079 
00080     If the contents is valid UTF-8, or force_unicode is True, then the value
00081     will a string, otherwise it will be bytes.
00082 
00083     Upon error, this will deliver a text representation of the error,
00084     instead of failing.
00085     '''
00086     try:
00087         with open(path, 'rb') as f:
00088             contents = f.read().strip()
00089         if force_unicode:
00090             return contents.decode('UTF-8', errors='replace')
00091         try:
00092             return contents.decode('UTF-8')
00093         except UnicodeDecodeError:
00094             return contents
00095     except Exception as e:
00096         return 'Error: ' + str(e)
00097 

Here is the caller graph for this function:

def apport.hookutils.recent_logfile (   logfile,
  pattern,
  maxlines = 10000 
)
Extract recent messages from a logfile which match a regex.

pattern should be a "re" object. By default this catches at most the last
1000 lines, but this can be modified with a different maxlines argument.

Definition at line 456 of file hookutils.py.

00456 
00457 def recent_logfile(logfile, pattern, maxlines=10000):
00458     '''Extract recent messages from a logfile which match a regex.
00459 
00460     pattern should be a "re" object. By default this catches at most the last
00461     1000 lines, but this can be modified with a different maxlines argument.
00462     '''
00463     lines = ''
00464     try:
00465         tail = subprocess.Popen(['tail', '-n', str(maxlines), logfile],
00466                                 stdout=subprocess.PIPE)
00467         while tail.poll() is None:
00468             for line in tail.stdout:
00469                 line = line.decode('UTF-8', errors='replace')
00470                 if pattern.search(line):
00471                     lines += line
00472         tail.stdout.close()
00473         tail.wait()
00474     except IOError:
00475         return ''
00476     return lines
00477 

Here is the caller graph for this function:

def apport.hookutils.recent_syslog (   pattern)
Extract recent messages from syslog which match a regex.

pattern should be a "re" object.

Definition at line 448 of file hookutils.py.

00448 
00449 def recent_syslog(pattern):
00450     '''Extract recent messages from syslog which match a regex.
00451 
00452     pattern should be a "re" object.
00453     '''
00454     return recent_logfile('/var/log/syslog', pattern)
00455 

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.hookutils.root_command_output (   command,
  input = None,
  stderr = subprocess.STDOUT,
  decode_utf8 = True 
)
Try to execute given command (array) as root and return its stdout.

This passes the command through gksu, kdesudo, or sudo, depending on the
running desktop environment.

In case of failure, a textual error gets returned.

If decode_utf8 is True (default), the output will be converted to a string,
otherwise left as bytes.

Definition at line 391 of file hookutils.py.

00391 
00392 def root_command_output(command, input=None, stderr=subprocess.STDOUT, decode_utf8=True):
00393     '''Try to execute given command (array) as root and return its stdout.
00394 
00395     This passes the command through gksu, kdesudo, or sudo, depending on the
00396     running desktop environment.
00397 
00398     In case of failure, a textual error gets returned.
00399 
00400     If decode_utf8 is True (default), the output will be converted to a string,
00401     otherwise left as bytes.
00402     '''
00403     assert isinstance(command, list), 'command must be a list'
00404     return command_output(_root_command_prefix() + command, input, stderr,
00405                           keep_locale=True, decode_utf8=decode_utf8)
00406 

Here is the call graph for this function:

Returns a list of strings containing the sonames of shared libraries
with which the specified binary is linked.

Definition at line 736 of file hookutils.py.

00736 
00737 def shared_libraries(path):
00738     '''Returns a list of strings containing the sonames of shared libraries
00739     with which the specified binary is linked.'''
00740 
00741     libs = set()
00742 
00743     for line in command_output(['ldd', path]).split('\n'):
00744         try:
00745             lib, rest = line.split('=>', 1)
00746         except ValueError:
00747             continue
00748 
00749         lib = lib.strip()
00750         libs.add(lib)
00751 
00752     return libs
00753 

Here is the call graph for this function:

Here is the caller graph for this function:

Return a text dump of USB devices attached to the system.

Definition at line 550 of file hookutils.py.

00550 
00551 def usb_devices():
00552     '''Return a text dump of USB devices attached to the system.'''
00553 
00554     # TODO: would be nice to be able to filter by interface class
00555     return command_output(['lsusb', '-v'])
00556 

Here is the call graph for this function:

def apport.hookutils.xsession_errors (   pattern = None)
Extract messages from ~/.xsession-errors.

By default this parses out glib-style warnings, errors, criticals etc. and
X window errors.  You can specify a "re" object as pattern to customize the
filtering.

Please note that you should avoid attaching the whole file to reports, as
it can, and often does, contain sensitive and private data.

Definition at line 478 of file hookutils.py.

00478 
00479 def xsession_errors(pattern=None):
00480     '''Extract messages from ~/.xsession-errors.
00481 
00482     By default this parses out glib-style warnings, errors, criticals etc. and
00483     X window errors.  You can specify a "re" object as pattern to customize the
00484     filtering.
00485 
00486     Please note that you should avoid attaching the whole file to reports, as
00487     it can, and often does, contain sensitive and private data.
00488     '''
00489     path = os.path.expanduser('~/.xsession-errors')
00490     if not os.path.exists(path):
00491         return ''
00492 
00493     if not pattern:
00494         pattern = re.compile('^(\(.*:\d+\): \w+-(WARNING|CRITICAL|ERROR))|(Error: .*No Symbols named)|([^ ]+\[\d+\]: ([A-Z]+):)|([^ ]-[A-Z]+ \*\*:)|(received an X Window System error)|(^The error was \')|(^  \(Details: serial \d+ error_code)')
00495 
00496     lines = ''
00497     with open(path, 'rb') as f:
00498         for line in f:
00499             line = line.decode('UTF-8', errors='replace')
00500             if pattern.search(line):
00501                 lines += line
00502     return lines

Here is the caller graph for this function:


Variable Documentation

Definition at line 36 of file hookutils.py.

Definition at line 510 of file hookutils.py.

Definition at line 508 of file hookutils.py.

Definition at line 505 of file hookutils.py.

Definition at line 512 of file hookutils.py.

Definition at line 511 of file hookutils.py.

Definition at line 503 of file hookutils.py.

Definition at line 507 of file hookutils.py.

Definition at line 506 of file hookutils.py.

Definition at line 504 of file hookutils.py.

Definition at line 513 of file hookutils.py.

Definition at line 514 of file hookutils.py.

Definition at line 509 of file hookutils.py.