--
+chiark-utils (5.0.3~citrix3) unstable; urgency=medium
+
+ fishdescriptor:
+ * New utility.
+
+ -- Ian Jackson <ian.jackson@citrix.com> Fri, 20 Apr 2018 16:46:48 +0100
+
chiark-utils (5.0.3~citrix2) unstable; urgency=medium
with-lock-ex:
Conflicts: chiark-named-conf, sync-accounts
Replaces: chiark-named-conf, sync-accounts
Depends: ${misc:Depends}
-Suggests: tcl8.4
+Suggests: tcl8.4, python3, gdb
Architecture: all
Description: chiark system administration scripts
This package contains a number of small administration scripts used
by chiark.greenend.org.uk and other systems belonging to the Sinister
Greenend Organisation. Featuring:
.
+ fishdescriptor: a tool for extracting a file descriptor from
+ another (non-cooperating) process and giving it to you (or
+ for examining it). Requires gdb and python3.
+ .
chiark-named-conf: a tool for managing nameserver configurations
and checking for suspected DNS problems. Its main functions are to
check that delegations are appropriate and working, that secondary
Copyright 2017 Ian Jackson in all jurisdictions
Copyright 2017 Genome Research Ltd
+fishdescriptor
+ Copyright 2018 Citrix
+
The chiark utilities are all free software; you can redistribute them
and/or modify them under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3 of the
SHELL=/bin/bash
subdirs_build_arch= cprogs
-subdirs_nobuild=backup sync-accounts scripts
+subdirs_nobuild=backup sync-accounts scripts fishdescriptor
package= chiark-utils
packages_indep= chiark-backup chiark-scripts
packages_arch= chiark-rwbuffer chiark-really chiark-utils-bin
#
mv $t/cprogs $t/chiark-utils-bin
#
+ cp -a debian/tmp/fishdescriptor/* debian/tmp/scripts/.
cp -a debian/tmp/sync-accounts/* debian/tmp/scripts/.
rm -r debian/tmp/sync-accounts
mv debian/tmp/scripts debian/tmp/chiark-scripts
--- /dev/null
+# Makefile
+
+# This file is part of chiark-utils, a collection of useful programs
+# used on chiark.greenend.org.uk.
+#
+# This file is:
+# Copyright (C) 2017 Ian Jackson <ian.jackson@eu.citrix.com>
+# Copyright (C) 2001 Ian Jackson <ijackson@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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 this program; if not, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+include ../settings.make
+
+SCRIPTS= fishdescriptor
+MODULES_23= __init__ indonor
+MODULES_3= fish
+
+py=py/fishdescriptor
+d2=$(python2dir)/fishdescriptor
+d3=$(python3dir)/fishdescriptor
+
+all:
+
+install:
+ $(INSTALL_DIRECTORY) $(bindir) $(d2) $(d3)
+ set -e; for f in $(SCRIPTS); do \
+ $(INSTALL_SCRIPT) $$f $(bindir)/$$f; done
+ set -e; for f in $(MODULES_3); do \
+ $(INSTALL_SHARE) $(py)/$$f.py $(d3)/.; done
+ set -e; for f in $(MODULES_23); do \
+ $(INSTALL_SHARE) $(py)/$$f.py $(d3)/.; \
+ ln $(d3)/$$f.py $(d2)/.; done
+
+install-docs:
+
+install-examples:
+
+clean:
+ rm -f *~ ./#*#
+ find -name \*.pyc -print0 | xargs -0r rm --
+
+distclean realclean: clean
--- /dev/null
+#!/usr/bin/python3
+
+# This file is part of chiark-utils, a collection of useful programs
+# used on chiark.greenend.org.uk.
+#
+# This file is:
+# Copyright 2018 Citrix Systems Ltd
+#
+# This 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, or (at your option) any later version.
+#
+# This 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 this program; if not, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+import sys
+import fishdescriptor.fish
+import optparse
+import re
+import subprocess
+import socket
+import os
+
+donor = None
+
+usage = '''fishdescriptor [-p|--pid] <pid> <action>... [-p|--pid <pid> <action>...]
+
+<action>s
+ [<here-fd>=]<there-fd>
+ fish the openfile referenced by descriptor <there-fd> in
+ (the most recent) <pid> and keep a descriptor onto it;
+ and, optionally, give it the number <here-fd> for exec
+ exec <program> [<arg>...]
+ execute a process with each specified <here>
+ as an actual fd
+ sockinfo
+ calls getsockname/getpeername on the most recent
+ <there-fd>
+
+ -p|-pid <pid>
+ now attach to <pid>, detaching from previous pid
+'''
+
+pending = []
+# list of (nominal, there) where nominal might be None
+
+fdmap = { }
+# fdmap[nominal] = (actual, Donor, there)
+
+def implement_pending():
+ try: actuals = donor.fish([pend[1] for pend in pending])
+ except fishdescriptor.fish.Error as e:
+ print('fishdescriptor error: %s' % e, file=sys.stderr)
+ sys.exit(127)
+ assert(len(actuals) == len(pending))
+ for (nominal, there), actual in zip(pending, actuals):
+ overwriting_info = fdmap.get(nominal)
+ if overwriting_info is not None: os.close(overwriting_info[0])
+ fdmap[nominal] = (actual, donor, there)
+
+def implement_sockinfo(nominal):
+ (actual, tdonor, there) = fdmap[nominal]
+ # socket.fromfd requires the AF. But of course we don't know the AF.
+ # There isn't a sane way to get it in Python:
+ # https://utcc.utoronto.ca/~cks/space/blog/python/SocketFromFdMistake
+ # Rejected options:
+ # https://github.com/tiran/socketfromfd
+ # adds a dependency, not portable due to reliance on SO_DOMAIN
+ # call getsockname using ctypes
+ # no sane way to discover how to unpack sa_family_t
+ perl_script = '''
+ use strict;
+ use Socket;
+ use POSIX;
+ my $sa = getsockname STDIN;
+ exit 0 if !defined $sa and $!==ENOTSOCK;
+ my $family = sockaddr_family $sa;
+ print $family, "\n" or die $!;
+ '''
+ famp = subprocess.Popen(
+ stdin = actual,
+ stdout = subprocess.PIPE,
+ args = ['perl','-we',perl_script]
+ )
+ (output, dummy) = famp.communicate()
+ family = int(output)
+
+ sock = socket.fromfd(actual, family, 0)
+
+ print("[%s] %d sockinfo" % (tdonor.pid, there), end='')
+ for f in (lambda: socket.AddressFamily(family).name,
+ lambda: repr(sock.getsockname()),
+ lambda: repr(sock.getpeername())):
+ try: info = f()
+ except Exception as e: info = repr(e)
+ print("\t", info, sep='', end='')
+ print("")
+
+ sock.close()
+
+def permute_fds_for_exec():
+ actual2intended = { info[0]: nominal for nominal, info in fdmap.items() }
+ # invariant at the start of each loop iteration:
+ # for each intended (aka `nominal') we have processed:
+ # relevant open-file is only held in fd intended
+ # (unless `nominal' is None in which case it is closed)
+ # for each intended (aka `nominal') we have NOT processed:
+ # relevant open-file is only held in actual
+ # where actual = fdmap[nominal][0]
+ # and where actual2intended[actual] = nominal
+ # we can rely on processing each intended only once,
+ # since they're hash keys
+ # the post-condition is not really a valid state (fdmap
+ # is nonsense) but we call this function just before exec
+ for intended, (actual, tdonor, there) in fdmap.items():
+ if intended == actual:
+ continue
+ if intended is not None:
+ inway_intended = actual2intended.get(intended)
+ if inway_intended is not None:
+ inway_moved = os.dup(intended)
+ actual2intended[inway_moved] = inway_intended
+ fdmap[inway_intented][0] = inway_moved
+ os.dup2(actual, intended)
+ os.close(actual)
+
+def implement_exec(argl):
+ if donor is not None: donor.detach()
+ sys.stdout.flush()
+ permute_fds_for_exec()
+ os.execvp(argl[0], argl)
+
+def set_donor(pid):
+ global donor
+ if donor is not None: donor.detach()
+ donor = fishdescriptor.fish.Donor(pid, debug=ov.debug)
+
+def ocb_set_donor(option, opt, value, parser):
+ set_donor(value)
+
+ov = optparse.Values()
+
+def process_args():
+ global ov
+
+ m = None
+
+ def arg_matches(regexp):
+ nonlocal m
+ m = re.search(regexp, arg)
+ return m
+
+ op = optparse.OptionParser(usage=usage)
+
+ op.disable_interspersed_args()
+ op.add_option('-p','--pid', type='int', action='callback',
+ callback=ocb_set_donor)
+ op.add_option('-D','--debug', action='store_const',
+ dest='debug', const=sys.stderr)
+ ov.debug = None
+
+ args = sys.argv[1:]
+ last_nominal = None # None or (nominal,) ie None or (None,) or (int,)
+
+ while True:
+ (ov, args) = op.parse_args(args=args, values=ov)
+ if not len(args): break
+
+ arg = args.pop(0)
+
+ if donor is None:
+ set_donor(int(arg))
+ elif arg_matches(r'^(?:(\d+)=)?(\d+)?$'):
+ (nominal, there) = m.groups()
+ nominal = None if nominal is None else int(nominal)
+ there = int(there)
+ pending.append((nominal,there))
+ last_nominal = (nominal,)
+ elif arg == 'exec':
+ if not len(args):
+ op.error("exec needs command to run")
+ implement_pending()
+ implement_exec(args)
+ elif arg == 'sockinfo':
+ if last_nominal is None:
+ op.error('sockinfo needs a prior fd spec')
+ implement_pending()
+ implement_sockinfo(last_nominal[0])
+ else:
+ op.error("unknown argument/option `%s'" % arg)
+
+process_args()
--- /dev/null
+# fish.py
+
+# This file is part of chiark-utils, a collection of useful programs
+# used on chiark.greenend.org.uk.
+#
+# This file is:
+# Copyright 2018 Citrix Systems Ltd
+#
+# This 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, or (at your option) any later version.
+#
+# This 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 this program; if not, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# python 3 only
+
+import socket
+import subprocess
+import os
+import pwd
+import struct
+import tempfile
+import shutil
+import sys
+
+def _shuffle_fd3():
+ os.dup2(1,3)
+ os.dup2(2,1)
+
+class Error(Exception): pass
+
+class Donor():
+ def __init__(d, pid, debug=None):
+ d.pid = pid
+ if debug is None:
+ d._stderr = tempfile.TemporaryFile(mode='w+')
+ else:
+ d._stderr = None
+ d._sp = subprocess.Popen(
+ preexec_fn = _shuffle_fd3,
+ stdin = subprocess.PIPE,
+ stdout = subprocess.PIPE,
+ stderr = d._stderr,
+ close_fds = False,
+ args = ['gdb', '-p', str(pid), '-batch', '-ex',
+ 'python import fishdescriptor.indonor as id;'+
+ ' id.DonorImplementation().eval_loop()'
+ ]
+ )
+
+ def _eval_integer(d, expr):
+ try:
+ l = d._sp.stdout.readline()
+ if not len(l): raise Error('gdb process donor python repl quit')
+ if l != b'!\n': raise RuntimeError("indonor said %s" % repr(l))
+ d._sp.stdin.write(expr.encode('utf-8') + b'\n')
+ d._sp.stdin.flush()
+ l = d._sp.stdout.readline().rstrip(b'\n')
+ return int(l)
+ except Exception as e:
+ if d._stderr is not None:
+ d._stderr.seek(0)
+ shutil.copyfileobj(d._stderr, sys.stderr)
+ d._stderr.seek(0)
+ d._stderr.truncate()
+ raise e
+
+ def _eval_success(d, expr):
+ r = d._eval_integer(expr)
+ if r != 1: raise RuntimeError("eval of %s gave %d" % (expr, r))
+
+ def _geteuid(d):
+ return d._eval_integer('di.geteuid()')
+
+ def _ancilmsg(d, fds):
+ perl_script = '''
+ use strict;
+ use Socket;
+ use Socket::MsgHdr;
+ my $fds = pack "i*", @ARGV;
+ my $m = Socket::MsgHdr::pack_cmsghdr SOL_SOCKET, SCM_RIGHTS, $fds;
+ print join ", ", unpack "C*", $m;
+ '''
+ ap = subprocess.Popen(
+ stdin = subprocess.DEVNULL,
+ stdout = subprocess.PIPE,
+ args = ['perl','-we',perl_script] + [str(x) for x in fds]
+ )
+ (output, dummy) = ap.communicate()
+ return output.decode('utf-8')
+
+ def donate(d, path, fds):
+ ancil = d._ancilmsg(fds)
+ d._eval_success('di.donate(%s, [ %s ])'
+ % (repr(path), ancil))
+ return len(ancil.split(','))
+
+ def mkdir(d, path):
+ d._eval_integer('di.mkdir(%s)'
+ % (repr(path)))
+
+ def _exists(d, path):
+ try:
+ os.stat(path)
+ return True
+ except OSError as oe:
+ if oe.errno != os.errno.ENOENT: raise oe
+ return False
+
+ def _sock_dir(d, target_euid):
+ run_dir = '/run/user/%d' % target_euid
+ if d._exists(run_dir):
+ return run_dir + '/fishdescriptor'
+
+ try:
+ pw = pwd.getpwuid(target_euid)
+ return pw.pw_dir + '/.fishdescriptor'
+ except KeyError:
+ pass
+
+ raise RuntimeError(
+ 'cannot find good socket path - no /run/user/UID nor pw entry for target process euid %d'
+ % target_euid
+ )
+
+ def fish(d, fds):
+ # -> list of fds in our process
+
+ euid = d._geteuid()
+ sockdir = d._sock_dir(euid)
+ d.mkdir(sockdir)
+
+ sockname = '%s/%s,%d' % (sockdir, os.uname().nodename, d.pid)
+
+ target_root = '/proc/%d/root' % d.pid
+ if not d._exists(target_root):
+ target_root = ''
+
+ our_sockname = target_root + sockname
+
+ s = None
+ s2 = None
+
+ try:
+ try: os.remove(our_sockname)
+ except FileNotFoundError: pass
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.bind(our_sockname)
+ s.listen(1)
+
+ ancil_len = d.donate(our_sockname, fds)
+ (s2, dummy) = s.accept()
+ (msg, ancil, flags, sender) = s2.recvmsg(1, ancil_len)
+
+ got_fds = None
+ unpack_fmt = '%di' % len(fds)
+
+ for clvl, ctype, cdata in ancil:
+ if clvl == socket.SOL_SOCKET and ctype == socket.SCM_RIGHTS:
+ assert(got_fds is None)
+ got_fds = struct.unpack_from(unpack_fmt, cdata)
+
+ finally:
+ if s is not None: s.close()
+ if s2 is not None: s2.close()
+
+ try: os.remove(our_sockname)
+ except FileNotFoundError: pass
+
+ return list(got_fds)
+
+ def detach(d):
+ d._sp.stdin.close()
--- /dev/null
+
+# This file is part of chiark-utils, a collection of useful programs
+# used on chiark.greenend.org.uk.
+#
+# This file is:
+# Copyright 2018 Citrix Systems Ltd
+#
+# This 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, or (at your option) any later version.
+#
+# This 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 this program; if not, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# class for use inside gdb which is debugging the donor process
+
+from __future__ import print_function
+
+import gdb
+import copy
+import os
+import sys
+import socket
+
+def _string_bytearray(s):
+ # gets us bytes in py2 and py3
+ if not isinstance(s, bytes):
+ s = s.encode('utf-8') # sigh, python 2/3 compat
+ return bytearray(s)
+
+def _string_escape_for_c(s):
+ out = ''
+ for c in _string_bytearray(s):
+ if c == ord('\\') or c == ord('"') or c < 32 or c > 126:
+ out += '\\x%02x' % c
+ else:
+ out += chr(c)
+ return out
+
+# constructing values
+
+def _lit_integer(v):
+ return '%d' % v
+
+def _lit_aggregate_uncasted(val_lit_strs):
+ return '{' + ', '.join(['(%s)' % v for v in val_lit_strs]) + ' }'
+
+def _lit_string_uncasted(s):
+ b = _string_bytearray(s)
+ return _lit_aggregate_uncasted([_lit_integer(x) for x in b] + [ '0' ])
+
+def _lit_array(elemtype, val_lit_strs):
+ return (
+ '((%s[%d])%s)' %
+ (elemtype, len(val_lit_strs), _lit_aggregate_uncasted(val_lit_strs))
+ )
+
+def _lit_addressof(v):
+ return '&(char[])(%s)' % v
+
+def _make_lit(v):
+ if isinstance(v, int):
+ return _lit_integer(v)
+ else:
+ return v # should already be an integer
+
+def parse_eval(expr):
+ sys.stderr.write("## EVAL %s\n" % repr(expr))
+ x = gdb.parse_and_eval(expr)
+ sys.stderr.write('## => %s\n' % x)
+ sys.stderr.flush()
+ return x
+
+class DonorStructLayout():
+ def __init__(l, typename):
+ x = gdb.lookup_type(typename)
+ l._typename = typename
+ l._template = [ ]
+ l._posns = { }
+ for f in x.fields():
+ l._posns[f.name] = len(l._template)
+ try: f.type.fields(); blank = '{ }'
+ except TypeError: blank = '0'
+ except AttributeError: blank = '0'
+ l._template.append(blank)
+ sys.stderr.write('## STRUCT %s template %s fields %s\n'
+ % (typename, l._template, l._posns))
+
+ def substitute(l, values):
+ build = copy.deepcopy(l._template)
+ for (k,v) in values.items():
+ build[ l._posns[k] ] = _make_lit(v)
+ return '((%s)%s)' % (l._typename, _lit_aggregate_uncasted(build))
+
+class DonorImplementation():
+ def __init__(di):
+ di._structs = { }
+ di._saved_errno = None
+ di._result_stream = os.fdopen(3, 'w')
+ di._errno_workaround = None
+
+ # assembling structs
+ # sigh, we have to record the order of the arguments!
+ def _find_fields(di, typename):
+ try:
+ fields = di._structs[typename]
+ except KeyError:
+ fields = DonorStructLayout(typename)
+ di._structs[typename] = fields
+ return fields
+
+ def _make(di, typename, values):
+ fields = di._find_fields(typename)
+ return fields.substitute(values)
+
+ # hideous workaround
+
+ def _parse_eval_errno(di, expr_pat):
+ # evaluates expr_pat % 'errno'
+ if di._errno_workaround is not True:
+ try:
+ x = parse_eval(expr_pat % 'errno')
+ di._errno_workaround = False
+ return x
+ except gdb.error as e:
+ if di._errno_workaround is False:
+ raise e
+ di._errno_workaround = True
+ # Incomprehensibly, gdb.parse_and_eval('errno') can sometimes
+ # fail with
+ # gdb.error: Cannot find thread-local variables on this target
+ # even though plain gdb `print errno' works while `print errno = 25'
+ # doesn't. OMG. This may be related to:
+ # https://github.com/cloudburst/libheap/issues/24
+ # although I can't find it in the gdb bug db (which is half-broken
+ # in my browser). Also the error is very nonspecific :-/.
+ # This seems to happen on jessie, and is fixed in stretch.
+ # Anyway:
+ return parse_eval(expr_pat % '(*((int (*)(void))__errno_location)())')
+
+ # calling functions (need to cast the function name to the right
+ # type in case maybe gdb doesn't know the type)
+
+ def _func(di, functype, funcname, realargs):
+ expr = '((%s) %s) %s' % (functype, funcname, realargs)
+ return parse_eval(expr)
+
+ def _must_func(di, functype, funcname, realargs):
+ retval = di._func(functype, funcname, realargs)
+ if retval < 0:
+ errnoval = di._parse_eval_errno('%s')
+ raise RuntimeError("%s gave errno=%d `%s'" %
+ (funcname, errnoval, os.strerror(errnoval)))
+ return retval
+
+ # wrappers for the syscalls that do what we want
+
+ def _sendmsg(di, carrier, control_msg):
+ iov_base = _lit_array('char', [1])
+ iov = di._make('struct iovec', {
+ 'iov_base': iov_base,
+ 'iov_len' : 1,
+ })
+
+ msg = di._make('struct msghdr', {
+ 'msg_iov' : _lit_addressof(iov),
+ 'msg_iovlen' : 1,
+ 'msg_control' : _lit_array('char', control_msg),
+ 'msg_controllen': len(control_msg),
+ })
+
+ di._must_func(
+ 'ssize_t (*)(int, const struct msghdr*, int)',
+ 'sendmsg',
+ '(%s, %s, 0)' % (carrier, _lit_addressof(msg))
+ )
+
+ def _socket(di):
+ return di._must_func(
+ 'int (*)(int, int, int)',
+ 'socket',
+ '(%d, %d, 0)' % (socket.AF_UNIX, socket.SOCK_STREAM)
+ )
+
+ def _connect(di, fd, path):
+ addr = di._make('struct sockaddr_un', {
+ 'sun_family' : _lit_integer(socket.AF_UNIX),
+ 'sun_path' : _lit_string_uncasted(path),
+ })
+
+ di._must_func(
+ 'int (*)(int, const struct sockaddr*, socklen_t)',
+ 'connect',
+ '(%d, (const struct sockaddr*)%s, sizeof(struct sockaddr_un))'
+ % (fd, _lit_addressof(addr))
+ )
+
+ def _close(di, fd):
+ di._must_func('int (*)(int)', 'close', '(%d)' % fd)
+
+ def _mkdir(di, path, mode):
+ r = di._func(
+ 'int (*)(const char*, mode_t)',
+ 'mkdir',
+ '("%s", %d)' % (_string_escape_for_c(path), mode)
+ )
+ if r < 0:
+ errnoval = di._parse_eval_errno('%s')
+ if errnoval != os.errno.EEXIST:
+ raise RuntimeError("mkdir %s failed: `%s'" %
+ (repr(path), os.strerror(errnoval)))
+ return 0
+ return 1
+
+ def _errno_save(di):
+ di._saved_errno = di._parse_eval_errno('%s')
+
+ def _errno_restore(di):
+ to_restore = di._saved_errno
+ di._saved_errno = None
+ if to_restore is not None:
+ di._parse_eval_errno('%%s = %d' % to_restore)
+
+ def _result(di, output):
+ sys.stderr.write("#> %s" % output)
+ di._result_stream.write(output)
+ di._result_stream.flush()
+
+ # main entrypoints
+
+ def donate(di, path, control_msg):
+ # control_msg is an array of integers being the ancillary data
+ # array ("control") for sendmsg, and hence specifies which fds
+ # to pass
+
+ carrier = None
+ try:
+ di._errno_save()
+ carrier = di._socket()
+ di._connect(carrier, path)
+ di._sendmsg(carrier, control_msg)
+ di._close(carrier)
+ carrier = None
+ finally:
+ if carrier is not None:
+ try: di._close(carrier)
+ except Exception: pass
+ di._errno_restore()
+
+ di._result('1\n')
+
+ def geteuid(di):
+ try:
+ di._errno_save()
+ val = di._must_func('uid_t (*)(void)', 'geteuid', '()')
+ finally:
+ di._errno_restore()
+
+ di._result('%d\n' % val)
+
+ def mkdir(di, path):
+ try:
+ di._errno_save()
+ val = di._mkdir(path, int('0700', 8))
+ finally:
+ di._errno_restore()
+
+ di._result('%d\n' % val)
+
+ def _protocol_read(di):
+ input = sys.stdin.readline()
+ if input == '': return None
+ input = input.rstrip('\n')
+ sys.stderr.write("#< %s\n" % input)
+ return input
+
+ def eval_loop(di):
+ if not gdb.selected_inferior().was_attached:
+ print('gdb inferior not attached', file=sys.stderr)
+ sys.exit(0)
+ while True:
+ di._result('!\n')
+ cmd = di._protocol_read()
+ if cmd is None: break
+ eval(cmd)
sharedir=$(prefix)/share/$(us)
perl5dir=$(prefix)/share/perl5
txtdocdir=$(prefix)/share/doc/$(us)
+python2dir=$(prefix)/lib/python2.7/dist-packages
+python3dir=$(prefix)/lib/python3/dist-packages
exampledir=$(txtdocdir)/examples
vardir=$(varlib)/$(us)
mandir=${prefix}/man