bigbro 0.5.2

Library for tracking file accesses of processes
Documentation
#!/usr/bin/python3

# bigbro filetracking library
# Copyright (C) 2015,2016 David Roundy
#
# This program 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 2 of the
# License, or (at your option) any later version.
#
# This program 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA

import string, os, glob, sys, importlib

def is_in_path(program):
    """ Does the program exist in the PATH? """
    def is_exe(fpath):
        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
    fpath, fname = os.path.split(program)
    if fpath:
        return is_exe(program)
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return True
    return False

platform = sys.platform
if platform == 'linux2':
    platform = 'linux'

os.system('rm -rf testing-flags');
os.mkdir('testing-flags');
with open('testing-flags/test.c', 'w') as f:
    f.write("""int main() {
  return 0;
}
""")

cc = 'gcc'

cflags = '$CFLAGS'
for flag in ['-O2', '-Wall', '-Werror', '-std=c99', '-g', '-mtune=native', '-fpic']:
    if not os.system('cd testing-flags && %s %s %s -c test.c' %
                     (cc, cflags, flag)):
        cflags += ' ' + flag
    else:
        print('# %s cannot use flag: %s' % (cc, flag))

print('# cc=', repr(cc)) # in future, we should set this dynamically
print('# cflags=', repr(cflags))

print("""
| %s %s -c bigbro-%s.c
< syscalls/linux.h
> bigbro-%s.o

# We need to remove libbigbro.a before running ar, because otherwise
# it will be added to, rather than replaced.

| rm -f libbigbro.a && ${AR-ar} rc libbigbro.a bigbro-%s.o && ${RANLIB-ranlib} libbigbro.a
< bigbro-%s.o
> libbigbro.a
""" % (cc, cflags, platform, platform, platform, platform))

print("""
| %s %s -static -o bigbro -L. fileaccesses.c -lbigbro
< libbigbro.a
> bigbro
""" % (cc, cflags))

print("""
| %s %s -shared -o libbigbro.so bigbro-%s.o
< bigbro-%s.o
> libbigbro.so
""" % (cc, cflags, platform, platform))


winlibraryfiles = ['bigbro-windows.c', 'win32/inject.c',
                   'win32/queue.c', 'win32/create_dlls.c', 'win32/dll_paths.c']
wincfiles = winlibraryfiles + ['fileaccesses.c']
dll_cfiles = ['win32/inject.c', 'win32/dll.c', 'win32/patch.c', 'win32/hooks.c',
              'win32/queue.c', 'win32/dll_paths.c']

if is_in_path('i686-w64-mingw32-gcc'):
    print('\n# We have a 32-bit mingw compiler, so let us cross-compile for windows!\n')

    cflags = ''
    for flag in ['-Wall', '-Werror', '-std=c99', '-g', '-O2']:
        if not os.system('cd testing-flags && i686-w64-mingw32-gcc %s %s -c test.c' %
                         (cflags, flag)):
            cflags += ' ' + flag
    else:
        print('# i686-w64-mingw32-gcc cannot use flag: %s' % flag)

    print('''
# first build the helper executable
| i686-w64-mingw32-gcc %s -c -o win32/helper32.obj win32/helper.c

| i686-w64-mingw32-gcc -o win32/helper.exe win32/helper32.obj
< win32/helper32.obj

# now convert this executable into a header file
| python3 build/binary2header.py win32/helper.exe win32/helper.h helper
< win32/helper.exe
''' % (cflags))

    for c in dll_cfiles:
          print("""
| i686-w64-mingw32-gcc %s -c -o %s32.obj %s
""" % (cflags, c[:-2], c))

    print('''
| i686-w64-mingw32-gcc -shared -o bigbro32.dll %s -lntdll -lpsapi'''
          % ' '.join([c[:-2]+'32.obj' for c in dll_cfiles]))
    for c in dll_cfiles:
        print("< %s32.obj" % c[:-2])

if is_in_path('x86_64-w64-mingw32-gcc'):
    print('\n# We have a 64-bit mingw compiler, so let us cross-compile for windows!\n')

    cflags = ''
    for flag in ['-Wall', '-Werror', '-std=c99', '-g', '-O2']:
        if not os.system('cd testing-flags && x86_64-w64-mingw32-gcc %s %s -c test.c' %
                         (cflags, flag)):
            cflags += ' ' + flag
    else:
        print('# x86_64-w64-mingw32-gcc cannot use flag: %s' % flag)

    for c in set(dll_cfiles+wincfiles):
          print("""
| x86_64-w64-mingw32-gcc %s -c -o %s.obj %s
> %s.obj
""" % (cflags, c[:-2], c, c[:-2]))
          if c == 'win32/create_dlls.c':
              print('< win32/bigbro32.h')
              print('< win32/bigbro64.h')
              print('< win32/helper.h')

    print('''
| x86_64-w64-mingw32-gcc -shared -o bigbro64.dll %s -lntdll -lpsapi'''
          % ' '.join([c[:-2]+'.obj' for c in dll_cfiles]))
    for c in dll_cfiles:
        print("< %s.obj" % c[:-2])

    print('''
# convert the dlls into into header files
| python3 build/binary2header.py bigbro32.dll win32/bigbro32.h bigbro32dll
< bigbro32.dll
| python3 build/binary2header.py bigbro64.dll win32/bigbro64.h bigbro64dll
< bigbro64.dll
''')

    print("""
| x86_64-w64-mingw32-gcc -o bigbro.exe fileaccesses.obj libbigbro-windows.a
< fileaccesses.obj
< libbigbro-windows.a
""")
    for c in wincfiles:
        print("< %s.obj" % c[:-2])
    print("""
# We need to remove libbigbro.a before running ar, because otherwise
# it will be added to, rather than replaced.

| rm -f libbigbro-windows.a && x86_64-w64-mingw32-gcc-ar rc libbigbro-windows.a %s && x86_64-w64-mingw32-gcc-ranlib libbigbro-windows.a
> libbigbro-windows.a
""" % (' '.join([c[:-1]+'obj' for c in winlibraryfiles])))
    for c in winlibraryfiles:
        print("< %s.obj" % c[:-2])


    for c in glob.glob('tests/*.c'):
        base = c[:-2]
        m = importlib.import_module('tests.'+base[6:])
        if 'skip_windows' in dir(m):
            print('# skipping test', base, 'not supported by windows')
        else:
            print("""
| x86_64-w64-mingw32-gcc %s -o %s-test.exe %s"""
                  % (cflags, c[:-2], c))

os.system('rm -rf testing-flags')

if is_in_path('sass'):
    print('''
| sass -I. web/style.scss web/style.css
> web/style.css
C .sass-cache
''')
else:
    print("# no sass, so we won't build style.css")

if is_in_path('doxygen'):
    print('''
| doxygen build/Doxyfile
> web/doxy/bigbro_8h.html
< build/doxy/header.html

| cp web/doxy/bigbro_8h.html web/documentation.html
< web/doxy/bigbro_8h.html
> web/documentation.html
''')

def cargo_cmd(cmd, inps, outs):
        print('\n| {}'.format(cmd))
        for i in inps:
            print("< {}".format(i))
        for o in outs:
            print("> {}".format(o))
        print('''c ~
c #
c .tum
c .pyc
c .gcda
c .gcno
c .gcov
c -bigbro
c Cargo.lock
C bench
C tests
C target
C web''')

if is_in_path('cargo'):
    cargo_cmd("cargo build && cp target/debug/test-bigbro .",
              ["syscalls/linux.h"],
              ["test-bigbro"])
    cargo_cmd("cargo doc --no-deps && cp -a target/doc web/", [],
              ["web/doc/bigbro/index.html"])
    cargo_cmd("cargo build --release && cp target/release/test-bigbro release-bigbro",
              ["syscalls/linux.h"],
              ["release-bigbro"])
else:
    print('# no cargo, so cannot build using rust')