├── 00README ├── AUTHORS ├── Changelog ├── MANIFEST.in ├── Makefile ├── README.apache-mod_wsgi ├── TODO ├── bin ├── dep-checker.py └── readelf.py ├── compliance ├── __init__.py ├── apache │ └── django.wsgi ├── linkage │ ├── __init__.py │ ├── admin.py │ ├── fixtures │ │ └── initial_data.xml │ ├── models.py │ ├── site_settings.py │ ├── templatetags │ │ ├── __init__.py │ │ └── custom_tags.py │ ├── tests.py │ └── views.py ├── load_static.py ├── media │ ├── css │ │ ├── barstyle.css │ │ ├── bodystyle.css │ │ ├── containers.css │ │ ├── docstyle.css │ │ ├── jqueryFileTree.css │ │ └── tabstyle.css │ ├── docs │ │ ├── Makefile │ │ ├── index.html.base │ │ ├── index.html.footer │ │ ├── policy_flag.png │ │ └── text-docs-to-html │ ├── images │ │ ├── bg.png │ │ ├── filetree │ │ │ ├── application.png │ │ │ ├── bg.png │ │ │ ├── code.png │ │ │ ├── css.png │ │ │ ├── db.png │ │ │ ├── directory.png │ │ │ ├── doc.png │ │ │ ├── file.png │ │ │ ├── film.png │ │ │ ├── flash.png │ │ │ ├── folder_open.png │ │ │ ├── html.png │ │ │ ├── java.png │ │ │ ├── linux.png │ │ │ ├── music.png │ │ │ ├── pdf.png │ │ │ ├── php.png │ │ │ ├── picture.png │ │ │ ├── ppt.png │ │ │ ├── psd.png │ │ │ ├── ruby.png │ │ │ ├── script.png │ │ │ ├── spinner.gif │ │ │ ├── txt.png │ │ │ ├── xls.png │ │ │ └── zip.png │ │ ├── left.png │ │ ├── left_on.png │ │ ├── lfc_dep_checker.jpg │ │ ├── lfc_dep_checker_small.png │ │ ├── lfc_dep_checker_small.png.org │ │ ├── lfdc_logo.jpg │ │ ├── lfdc_logo_large.png │ │ ├── lfdc_logo_small.png │ │ ├── lflogo.png │ │ ├── orange_flag.png │ │ ├── red_flag.png │ │ ├── right.png │ │ └── right_on.png │ └── js │ │ ├── checklist.js │ │ ├── jquery-1.4.2.min.js │ │ ├── jqueryFileTree.js │ │ └── visibility.js ├── settings.py ├── task.py ├── templates │ ├── admin │ │ └── base_site.html │ └── linkage │ │ ├── banner.html │ │ ├── base.html │ │ ├── detail.html │ │ ├── documentation.html │ │ ├── header.html │ │ ├── index.html │ │ ├── lbindings.html │ │ ├── licenses.html │ │ ├── noscript.html │ │ ├── policy.html │ │ ├── results.html │ │ ├── settings.html │ │ └── test.html └── urls.py ├── desktop ├── dep-checker.desktop └── lf_small.png ├── doc ├── Contributing └── LICENSE ├── manage.py ├── package ├── Makefile ├── dep-checker.spec.sed ├── rpmmacros └── rpmrc └── setup.py /00README: -------------------------------------------------------------------------------- 1 | Layout/Concept 2 | 3 | 2 part app: cli/gui 4 | 5 | The cli command, readelf.py, should be standard python. It does run 6 | some system commands like readelf, file, and ldd. 7 | 8 | gui all lives under "compliance". 9 | 10 | Both the web app and the cli rely on Django 1.8. Older or newer 11 | versions of Django may work, but changes to the APIs may cause 12 | problems. Because of this dependency, Python 2.7 is strongly 13 | recommended as well. 14 | 15 | By default, typing "make" in the top-level will create the SQLite 16 | database both apps need. 17 | 18 | linkage: bulk of the gui code 19 | models.py is the database/form models 20 | views.py is the data/form processing code 21 | 22 | media: css/images/docs 23 | 24 | templates: html pages using django's "template" language for data/form 25 | handling (these also have some javascript glue) 26 | 27 | The web app can be run three ways: 28 | 29 | - "In-place" (see below) 30 | 31 | - Installed using the normal "python setup.py install". 32 | 33 | - Run from /opt/linuxfoundation/compliance. 34 | 35 | To run the gui/server (as user compliance for installed package): 36 | /opt/linuxfoundation/bin/deps-checker.py start (for running installed) 37 | 38 | To run the application in place, you *must* first run "make" to create the 39 | database and docs, as well as populate the db with the static data: 40 | make 41 | bin/deps-checker.py start (for running in-place) 42 | 43 | To run the application after "python setup.py install", use 44 | dep-checker.py from the usual binary directory. This is usually 45 | /usr/local/bin for regular installs, or the bin directory in a 46 | virtualenv. 47 | 48 | "deps-checker.py stop" will stop the server. 49 | 50 | to access the gui: http://127.0.0.1:8000/linkage 51 | admin interface: http://127/0.0.1:8000/admin 52 | (username compliance, password compliance) 53 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Stew Benedict 2 | Jeff Licquia 3 | -------------------------------------------------------------------------------- /Changelog: -------------------------------------------------------------------------------- 1 | 0.0.4: 2 | Change name to Dependency Checker Tool 3 | Drop Home, About tabs 4 | Drop copyright notice 5 | Add AUTHORS, Changelog 6 | Add bzr, bugzilla, mail list info to README.html 7 | Fix date/time display in detail, results 8 | Fix directory recursion 9 | New License file 10 | Add contribution requirements 11 | Rework dep recursion in command line app 12 | Fix test.html so it displays ok on most browsers 13 | Workaround weird display issue for results,detail in firefox 3.6.3 14 | Fix detail page printing, suppress unwanted elements with css 15 | Add new logo 16 | Detect/use local timezone settings 17 | Add directory/file browsing to test input form 18 | 0.0.5: 19 | Add license/license policy tabs 20 | Rework documentation 21 | Add dep-server-stop.sh to stop running server instances 22 | Add target/library license binding tabs and tables 23 | More GUI tweaks for more tabs 24 | Insert file/library bindings into test data after run 25 | Add policy violation flagging to test results 26 | Expose --no-static in the GUI 27 | Add license<-->alias mapping/reporting capability (bug 459) 28 | Add approved/disapproved policy highlighting (bug 475) 29 | Pre-load with license/alias data (bug 478) 30 | Enable data collection from the command line (bug 472) 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include doc/LICENSE 2 | include 00README 3 | recursive-include compliance/media * 4 | recursive-include compliance/templates * 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Top-level Makefile for dep-checker. 2 | # Copyright 2010 The Linux Foundation. See LICENSE file for licensing. 3 | 4 | PYTHON ?= $(shell which python) 5 | 6 | default: compliance/compliance compliance/media/docs/index.html README.txt 7 | 8 | package: 9 | cd package && $(MAKE) 10 | 11 | compliance/compliance: compliance/linkage/models.py compliance/linkage/fixtures/initial_data.xml 12 | rm -f compliance/compliance 13 | $(PYTHON) manage.py syncdb --noinput 14 | 15 | fixture_regen: 16 | $(PYTHON) manage.py dumpdata --format xml linkage | \ 17 | xmllint --format - > compliance/linkage/fixtures/initial_data.xml 18 | 19 | compliance/media/docs/index.html: 20 | cd compliance/media/docs && $(MAKE) 21 | 22 | README.txt: compliance/media/docs/index.html 23 | w3m -dump $< > $@ 24 | 25 | clean: 26 | cd package && $(MAKE) clean 27 | cd compliance/media/docs && $(MAKE) clean 28 | rm -f README.txt 29 | rm -f compliance/compliance 30 | 31 | .PHONY: default clean package fixture_regen 32 | -------------------------------------------------------------------------------- /README.apache-mod_wsgi: -------------------------------------------------------------------------------- 1 | Using dep-checker with apache/mod_wsgi 2 | 7/23/2010 Stew Benedict 3 | 4 | Instead of the built in django server, you can use apache and mod_wsgi fairly easily. 5 | 6 | 1) Put the dep-checker tree under DocumentRoot (or anywhere accessible to apache, in the 7 | example we use /var/www/html) 8 | 9 | 2) Edit settings.py and add the working directory to project_root_paths: 10 | 11 | project_root_paths = [ ".", "..", "/var/www/html/dep-checker" ] 12 | 13 | 3) Edit http.conf or possibly a seperate conf for mod_wsgi (/etc/httpd/modules.d/B23_mod_wsgi.conf 14 | on this system): 15 | 16 | WSGIScriptAlias / /var/www/html/dep-checker/compliance/apache/django.wsgi 17 | 18 | The above line is required. The referenced file is provided by dep-checker. There are 2 19 | sys.path.append entries you may need to modify for your setup. 20 | 21 | Some older versions of mod_wsgi will not work for applications using stdout (which dep-checker 22 | does to communicate with the task-manager). If your version has this issue, you will want to 23 | add the following directive: 24 | 25 | WSGIRestrictStdout Off 26 | 27 | If you find performance to be slow, you may want to run wsgi in daemon mode: 28 | 29 | WSGIDaemonProcess example.com threads=25 30 | WSGIProcessGroup example.com 31 | 32 | More info on mod_wsgi configuration can be found here: 33 | 34 | http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide 35 | 36 | 4) The sqlite database compliance/compliance must be writable by the user account running apache 37 | (apache on this system). The compliance directory must also be writable by this user for both 38 | database journal and the task.log file used by the task manager. 39 | 40 | Documentation for setting up django with mod_wsgi mentions serving the static media files by 41 | apache or another server, although in my experiments things seemed to work leaving things as they 42 | are currently setup. 43 | 44 | 45 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Known issues as of 6/24/2010 2 | 3 | GUI: 4 | 5 | Detail Page: 6 | flag license policy violations 7 | update license bindings when we run a test 8 | 9 | -------------------------------------------------------------------------------- /bin/dep-checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # dep-checker - command to start/stop the Dependency Checker web interface 4 | # Copyright 2010 Linux Foundation 5 | # Jeff Licquia 6 | 7 | import sys 8 | import os 9 | import pwd 10 | import time 11 | import signal 12 | import optparse 13 | import shutil 14 | 15 | from django.core.management import execute_from_command_line 16 | 17 | command_line_usage = "%prog [options] start | stop" 18 | command_line_options = [ 19 | optparse.make_option("--force-root", action="store_true", 20 | dest="force_root", default=False, 21 | help="allow running as root"), 22 | optparse.make_option("--server-only", action="store_true", 23 | dest="server_only", default=False, 24 | help="don't open a browser"), 25 | optparse.make_option("--interface", action="store", 26 | dest="interface", default=None, 27 | help="listen on network interface (port or ip:port)"), 28 | ] 29 | 30 | def get_base_path(): 31 | this_module_path = os.path.dirname(os.path.abspath(__file__)) 32 | return os.path.dirname(this_module_path) 33 | 34 | def set_import_path(): 35 | sys.path.append(get_base_path()) 36 | 37 | set_import_path() 38 | from compliance import settings 39 | 40 | def check_current_user(): 41 | if os.getuid() == 0: 42 | try: 43 | compliance_user = pwd.getpwnam("compliance") 44 | except KeyError: 45 | sys.stderr.write("Could not find user 'compliance'.\n") 46 | sys.exit(1) 47 | 48 | os.setuid(compliance_user.pw_uid) 49 | 50 | # Setting up userdir mode. 51 | 52 | def setup_userdir(): 53 | if not os.path.exists(settings.USERDIR_ROOT): 54 | os.mkdir(settings.USERDIR_ROOT) 55 | shutil.copyfile(os.path.join(settings.PROJECT_ROOT, 56 | "compliance", "compliance"), 57 | os.path.join(settings.USERDIR_ROOT, "compliance")) 58 | 59 | def start_server(run_browser, interface=None): 60 | pid_path = os.path.join(settings.STATE_ROOT, "server.pid") 61 | if os.path.exists(pid_path): 62 | server_pid = int(open(pid_path).read()) 63 | pid_found = False 64 | try: 65 | os.kill(server_pid, 0) 66 | pid_found = True 67 | except OSError: 68 | pid_found = False 69 | if pid_found: 70 | sys.stderr.write("The server is already running.\n") 71 | sys.exit(1) 72 | else: 73 | os.unlink(pid_path) 74 | 75 | if settings.USERDIR_ROOT: 76 | setup_userdir() 77 | 78 | childpid = os.fork() 79 | if childpid == 0: 80 | os.setsid() 81 | 82 | log_fn = os.path.join(settings.STATE_ROOT, "server.log") 83 | try: 84 | log_fd = os.open(log_fn, os.O_WRONLY | os.O_APPEND | os.O_CREAT) 85 | except OSError: 86 | log_fd = -1 87 | if log_fd < 0: 88 | sys.stderr.write("Could not open log file; logging to stdout.\n") 89 | else: 90 | os.dup2(log_fd, 1) 91 | os.dup2(log_fd, 2) 92 | 93 | os.close(0) 94 | 95 | manager_args = ["dep-checker", "runserver", "--noreload"] 96 | if interface: 97 | manager_args.append(interface) 98 | 99 | execute_from_command_line(manager_args) 100 | else: 101 | time.sleep(1) 102 | 103 | pid_file = open(pid_path, "w") 104 | pid_file.write(str(childpid)) 105 | pid_file.close() 106 | 107 | if run_browser: 108 | if interface: 109 | if interface.find(":") != -1: 110 | (ipaddr, port) = interface.split(":") 111 | if ipaddr == "0.0.0.0": 112 | interface = "127.0.0.1:" + port 113 | app_url = "http://%s/linkage" % interface 114 | else: 115 | app_url = "http://127.0.0.1:8000/linkage" 116 | sys.stdout.write("Waiting for the server to start...\n") 117 | time.sleep(10) 118 | sys.stdout.write("Starting a web browser.\n") 119 | os.execlp("xdg-open", "xdg-open", app_url) 120 | else: 121 | sys.exit(0) 122 | 123 | def stop_server(): 124 | pid_path = os.path.join(settings.STATE_ROOT, "server.pid") 125 | if os.path.exists(pid_path): 126 | server_pid = int(open(pid_path).read()) 127 | sys.stdout.write("Killing process %d...\n" % server_pid) 128 | try: 129 | try: 130 | os.kill(server_pid, signal.SIGTERM) 131 | finally: 132 | os.unlink(pid_path) 133 | except OSError, e: 134 | sys.stderr.write("Could not kill process: %s\n" % str(e)) 135 | sys.exit(1) 136 | else: 137 | sys.stderr.write("No server process found to stop.\n") 138 | sys.exit(1) 139 | 140 | def main(): 141 | cmdline_parser = optparse.OptionParser(usage=command_line_usage, 142 | option_list=command_line_options) 143 | (options, args) = cmdline_parser.parse_args() 144 | if len(args) != 1 or args[0] not in ["start", "stop"]: 145 | cmdline_parser.error("incorrect arguments") 146 | 147 | # Set up environment for Django to find settings. 148 | 149 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "compliance.settings") 150 | 151 | # Switch users if needed. 152 | 153 | if args[0] == "start": 154 | if not options.force_root: 155 | check_current_user() 156 | start_server(not options.server_only, options.interface) 157 | else: 158 | stop_server() 159 | 160 | if __name__ == "__main__": 161 | main() 162 | -------------------------------------------------------------------------------- /bin/readelf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # program to gather link dependencies of ELF files for compliance analysis 3 | # Stew Benedict 4 | # Jeff Licquia 5 | # copyright 2010 Linux Foundation 6 | 7 | import sys 8 | import os 9 | import re 10 | import string 11 | import sqlite3 12 | import optparse 13 | version = '0.0.8' 14 | 15 | # Get Django loaded. This has to be done outside a function so 16 | # the setup is only done once and the modules are globally available. 17 | 18 | os.environ["DJANGO_SETTINGS_MODULE"] = "compliance.settings" 19 | 20 | django_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 21 | sys.path.append(django_path) 22 | 23 | from compliance.linkage.models import StaticSymbol 24 | from compliance.linkage.views import create_test_record, delete_test_record, \ 25 | process_results, mark_test_done 26 | 27 | # Custom exceptions. 28 | 29 | class NotELFError(StandardError): 30 | pass 31 | 32 | class StaticSymbolError(StandardError): 33 | pass 34 | 35 | class NoDebugInfoError(StaticSymbolError): 36 | pass 37 | 38 | class NoStaticDataError(StaticSymbolError): 39 | pass 40 | 41 | # Globals. 42 | 43 | usage_line = "usage: %prog [options] [recursion depth]" 44 | 45 | command_line_options = [ 46 | optparse.make_option("-c", action="store_true", dest="do_csv", 47 | default=False, help="output in csv format"), 48 | optparse.make_option("-d", action="store_true", dest="do_db", 49 | default=False, help="write the output into the results database"), 50 | optparse.make_option("-s", action="store", type="string", dest="target", 51 | metavar="DIR", help="directory tree to search"), 52 | optparse.make_option("--comments", action="store", type="string", dest="comments", 53 | default = '', help="test comments (when writing to database)"), 54 | optparse.make_option("--project", action="store", type="string", dest="project", 55 | default = '', help="project name (when writing to database)"), 56 | optparse.make_option("--no-static", action="store_false", dest="do_static", 57 | default=True, help="don't look for static dependencies") 58 | ] 59 | 60 | depth = 1 61 | do_csv = False 62 | do_static = True 63 | do_search = False 64 | do_db = False 65 | project = '' 66 | comments = '' 67 | testid = None 68 | lastfile = '' 69 | parentid = 0 70 | parentlibid = 0 71 | 72 | def show_result(result): 73 | sys.stdout.write(result + "\n") 74 | sys.stdout.flush() 75 | if do_db: 76 | # we need all these for the recursive case, now that we write a line at a time to the db 77 | global lastfile, parentid, parentlibid 78 | errmsg = '' 79 | 80 | errmsg, lastfile, parentid, parentlibid = process_results(result, testid, lastfile, parentid, parentlibid) 81 | 82 | if errmsg: 83 | show_error(errmsg) 84 | 85 | def show_error(msg): 86 | sys.stdout.write(msg + "\n") 87 | sys.stdout.flush() 88 | if do_db: 89 | delete_test_record(testid) 90 | sys.exit(1) 91 | 92 | def bad_depth(): 93 | show_error("Recursion depth must be a positive number") 94 | 95 | def dep_path(target, dep): 96 | # readelf gives us the lib, but not the path to check during recursion 97 | ldcall = "ldd " + target 98 | 99 | for lddata in os.popen(ldcall).readlines(): 100 | if re.search("statically linked", lddata): 101 | return "NONE" 102 | 103 | elif re.search(dep, lddata): 104 | ldlist = string.split(lddata) 105 | if len(ldlist) > 2: 106 | # path is the 3rd field, usually... 107 | dpath = ldlist[2] 108 | else: 109 | dpath = ldlist[0] 110 | # but this may be a symlink 111 | dpath = os.path.realpath(dpath) 112 | break 113 | 114 | return dpath 115 | 116 | def find_static_library(func): 117 | "Given a symbol, return the most likely static library it's from." 118 | 119 | found_libs = StaticSymbol.objects.filter(symbol=func) 120 | return [x.libraryname for x in found_libs] 121 | 122 | def static_deps_check(target): 123 | "Look for statically linked dependencies." 124 | 125 | # State enumeration for debug parser. 126 | FIND_NEXT = 1 127 | FIND_NAME = 2 128 | 129 | # The algorithm here is pretty basic. We grab a complete symbol list 130 | # and debug information. Any symbols that aren't covered by debug 131 | # information are considered to be source from static libraries. 132 | 133 | # Check that we have static symbol data. 134 | static_data_count = StaticSymbol.objects.all().count() 135 | if static_data_count < 1: 136 | raise NoStaticDataError, "no static symbol data" 137 | 138 | # Read the functions from the symbol list. 139 | symlist = [ x.split() for x in os.popen("readelf -s " + target) ] 140 | symlist = [ x for x in symlist if len(x) == 8 ] 141 | sym_funcs = set([ x[7] for x in symlist if x[3] == "FUNC" ]) 142 | 143 | # Read the functions from the debug information. 144 | found_debug_info = False 145 | debuginfo = os.popen("readelf -wi " + target) 146 | debug_funcs = set() 147 | debugstate = FIND_NEXT 148 | for line in debuginfo: 149 | if len(line) < 2: 150 | continue 151 | 152 | found_debug_info = True 153 | 154 | if debugstate == FIND_NAME: 155 | if line[1] == "<": 156 | debugstate = FIND_NEXT 157 | else: 158 | match = re.match(r'\s+<.+>\s+(.+?)\s+:\s+\(.+\):\s+(.+)$', line) 159 | if match: 160 | (field, value) = match.group(1, 2) 161 | if field == "DW_AT_name": 162 | debug_funcs.add(value.strip()) 163 | debugstate = FIND_NEXT 164 | 165 | if debugstate == FIND_NEXT and line[1] == "<": 166 | match = re.search(r'\((.+)\)$', line) 167 | if match and match.group(1) == "DW_TAG_subprogram": 168 | found_name = None 169 | debugstate = FIND_NAME 170 | 171 | # If no debug information was reported, report the error. 172 | if not found_debug_info: 173 | raise NoDebugInfoError, "no debugging information was found" 174 | 175 | # Get the functions in the symbol list that have no debug info. 176 | staticsym_funcs = sym_funcs - debug_funcs 177 | 178 | # For each function, figure out where it came from. 179 | staticlib_list = [] 180 | staticlib_multiples = {} 181 | for func in staticsym_funcs: 182 | libs = find_static_library(func) 183 | if len(libs) == 1: 184 | if libs[0] not in staticlib_list: 185 | staticlib_list.append(libs[0]) 186 | elif len(libs) > 1: 187 | staticlib_multiples[func] = libs 188 | 189 | # Symbols found in multiple libraries should be handled last. 190 | # We pick the first library only if none of the libs has 191 | # been picked yet. 192 | for func in staticlib_multiples: 193 | found = False 194 | for lib in staticlib_multiples[func]: 195 | if lib in staticlib_list: 196 | found = True 197 | break 198 | if not found: 199 | staticlib_list.append(staticlib_multiples[func][0]) 200 | 201 | # Format and return the list. 202 | staticlib_list.sort() 203 | staticlib_results = [ x + " (static)" for x in staticlib_list ] 204 | return staticlib_results 205 | 206 | def deps_check(target): 207 | deps = [] 208 | # run the "file" command and see if it's ELF 209 | filetype = os.popen("file " + target).read() 210 | 211 | if re.search("ELF", filetype): 212 | if not re.search("statically linked", filetype): 213 | elfcall = "readelf -d " + target 214 | for elfdata in os.popen(elfcall).readlines(): 215 | # lines we want all have "NEEDED" 216 | if re.search("NEEDED", elfdata): 217 | # library is the 5th field 218 | dep = string.split(elfdata)[4] 219 | dep = dep.strip("[]") 220 | deps.append(dep) 221 | 222 | if do_static: 223 | try: 224 | deps.extend(static_deps_check(target)) 225 | except StaticSymbolError: 226 | deps.append("WARNING: Could not check for static dependencies") 227 | 228 | else: 229 | raise NotELFError, "not an ELF file" 230 | 231 | return deps 232 | 233 | # non-recursive case 234 | def print_deps(target, deps): 235 | global rbuff 236 | csvstring = '' 237 | spacer = '' 238 | 239 | if len(deps) < 1: 240 | return 241 | 242 | if do_csv: 243 | csvstring += str(1) + "," + target 244 | for dep in deps: 245 | csvstring += "," + dep 246 | show_result(csvstring) 247 | 248 | else: 249 | show_result(spacer + "[" + str(1) + "]" + target + ":") 250 | spacer += " " 251 | for dep in deps: 252 | show_result(spacer + dep) 253 | 254 | def print_dep(dep, indent): 255 | spacer = 2 * indent * " " 256 | if not do_csv: 257 | show_result(spacer + dep) 258 | 259 | def print_path_dep(parent, soname, dep, indent): 260 | csvstring = '' 261 | spacer = (indent - 1) * " " 262 | token = "[" + str(indent) + "]" 263 | if not do_csv: 264 | show_result(spacer + token + parent + ":") 265 | else: 266 | csvstring += str(indent) + "," + parent + "," 267 | # indent = level, treat level 1 slightly differently 268 | if indent != 1 and soname: 269 | csvstring += soname + "," 270 | csvstring += dep 271 | show_result(csvstring) 272 | 273 | def dep_loop(parent, soname, dep, level): 274 | if level > depth: 275 | return 276 | 277 | if level == 1: 278 | print_path_dep(parent, soname, dep, level) 279 | print_dep(dep, level) 280 | else: 281 | print_path_dep(parent, soname, dep, level) 282 | print_dep(dep, level) 283 | 284 | if not re.search('(static)', dep): 285 | target = dep_path(parent, dep) 286 | childdeps = deps_check(target) 287 | else: 288 | childdeps = [] 289 | 290 | if len(childdeps) > 0: 291 | for childdep in childdeps: 292 | dep_loop(target, dep, childdep, level + 1) 293 | 294 | def db_test_record(target, target_dir): 295 | testid = 0 296 | user = os.environ["USER"] 297 | testid = create_test_record(do_search, not(do_static), depth, target, target_dir, user, project, comments) 298 | return testid 299 | 300 | def main(): 301 | opt_parser = optparse.OptionParser(usage=usage_line, 302 | version="%prog version " + version, 303 | option_list=command_line_options) 304 | (options, args) = opt_parser.parse_args() 305 | 306 | if len(args) == 0 or len(args) > 2: 307 | opt_parser.error("improper number of non-option arguments") 308 | 309 | # prog_ndx_start is the offset in argv for the file/dir and recursion 310 | prog_ndx_start = 1 311 | found = 0 312 | parent = '' 313 | target_dir = '' 314 | global do_csv, depth, do_static, do_search, do_db, project, comments, testid 315 | 316 | do_static = options.do_static 317 | do_csv = options.do_csv 318 | do_db = options.do_db 319 | # force csv in this case 320 | if do_db: 321 | do_csv = True 322 | project = options.project 323 | comments = options.comments 324 | 325 | if options.target: 326 | do_search = True 327 | target = options.target 328 | target_file = args[0] 329 | target_dir = target 330 | if not os.path.isdir(target): 331 | show_error(target + " does not appear to be a directory...") 332 | else: 333 | target = args[0] 334 | if not(os.path.isdir(target) or os.path.isfile(target)): 335 | show_error(target + " does not appear to be a directory or file...") 336 | 337 | # sanity check on recursion level 338 | if len(args) == 1: 339 | depth = 1 340 | 341 | else: 342 | try: 343 | recursion_arg = args[1] 344 | depth = int(recursion_arg) 345 | except: 346 | bad_depth() 347 | 348 | if depth < 1: 349 | bad_depth() 350 | 351 | if do_db: 352 | testid = db_test_record(target, target_dir) 353 | 354 | if os.path.isdir(target): 355 | # walk the directory tree and find ELF files to process 356 | for path, dirs, files in os.walk(target): 357 | for filename in files: 358 | if (do_search and (filename == target_file)) or not(do_search): 359 | candidate = os.path.join(path, filename) 360 | if os.path.isfile(candidate): 361 | try: 362 | deps = deps_check(candidate) 363 | except NotELFError: 364 | deps = [] 365 | 366 | if len(deps) > 0: 367 | if depth == 1: 368 | print_deps(candidate, deps) 369 | # do recursion if called for 370 | else: 371 | for dep in deps: 372 | dep_loop(candidate, None, dep, 1) 373 | if do_search and (filename == target_file): 374 | found = 1 375 | break 376 | 377 | if do_search and not found: 378 | show_error(target_file + " was not found in " + target + " ...") 379 | 380 | else: 381 | # single file, just check it and exit 382 | # top level deps 383 | parent = target 384 | target_file = target 385 | target_dir = '' 386 | 387 | try: 388 | deps = deps_check(target) 389 | except NotELFError: 390 | show_error("not an ELF file...") 391 | 392 | if depth == 1: 393 | print_deps(target, deps) 394 | 395 | # do recursion if called for 396 | else: 397 | for dep in deps: 398 | dep_loop(parent, None, dep, 1) 399 | 400 | # close the db test record 401 | if do_db: 402 | mark_test_done(testid) 403 | 404 | sys.exit(0) 405 | 406 | if __name__=='__main__': 407 | main() 408 | 409 | -------------------------------------------------------------------------------- /compliance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/__init__.py -------------------------------------------------------------------------------- /compliance/apache/django.wsgi: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' 5 | 6 | sys.path.append('/var/www/html/dep-checker') 7 | sys.path.append('/var/www/html/dep-checker/compliance') 8 | 9 | import django.core.wsgi 10 | application = django.core.wsgi.get_wsgi_application() 11 | -------------------------------------------------------------------------------- /compliance/linkage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/linkage/__init__.py -------------------------------------------------------------------------------- /compliance/linkage/admin.py: -------------------------------------------------------------------------------- 1 | from compliance.linkage.models import Test, File, Lib 2 | from django.contrib import admin 3 | 4 | admin.site.register(Test) 5 | admin.site.register(File) 6 | admin.site.register(Lib) 7 | -------------------------------------------------------------------------------- /compliance/linkage/fixtures/initial_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Academic Free License 3.0 5 | AFL 6 | 3.0 7 | 8 | 9 | Affero GNU Public License 10 | AGPL 11 | 12 | 13 | 14 | Adaptive Public License 15 | APL 16 | 17 | 18 | 19 | Apache License 20 | Apache 21 | 2.0 22 | 23 | 24 | Apple Public Source License 25 | Apple 26 | 27 | 28 | 29 | Artistic license 2.0 30 | Artistic 31 | 2.0 32 | 33 | 34 | Attribution Assurance Licenses 35 | AAL 36 | 37 | 38 | 39 | New and Simplified BSD licenses 40 | BSD 41 | 42 | 43 | 44 | Boost Software License 45 | BSL 46 | 1.0 47 | 48 | 49 | Computer Associates Trusted Open Source License 50 | CATOSL 51 | 1.1 52 | 53 | 54 | Common Development and Distribution License 55 | CDDL 56 | 57 | 58 | 59 | Common Public Attribution License 1.0 60 | CPAL 61 | 1.0 62 | 63 | 64 | CUA Office Public License Version 1.0 65 | CUAOPL 66 | 1.0 67 | 68 | 69 | EU DataGrid Software License 70 | EUDSL 71 | 72 | 73 | 74 | Eclipse Public License 75 | EPL 76 | 77 | 78 | 79 | Educational Community License Version 2.0 80 | ECL 81 | 2.0 82 | 83 | 84 | Eiffel Forum License V2.0 85 | EFL 86 | 2.0 87 | 88 | 89 | Entessa Public License 90 | EPL 91 | 92 | 93 | 94 | Fair License 95 | Fair 96 | 97 | 98 | 99 | Frameworx License 100 | Frameworx 101 | 102 | 103 | 104 | GNU General Public License (GPL) 105 | GPL 106 | 107 | 108 | 109 | GNU General Public License version 3.0 (GPLv3) 110 | GPL 111 | 3.0 112 | 113 | 114 | GNU Library or 'Lesser' General Public License (LGPL) 115 | LGPL 116 | 117 | 118 | 119 | GNU Library or 'Lesser' General Public License version 3.0 (LGPLv3) 120 | LGPL 121 | 3.0 122 | 123 | 124 | Historical Permission Notice and Disclaimer 125 | Historical 126 | 127 | 128 | 129 | IBM Public License 130 | IBM 131 | 132 | 133 | 134 | IPA Font License 135 | IPA 136 | 137 | 138 | 139 | ISC License 140 | ISC 141 | 142 | 143 | 144 | Lucent Public License Version 1.02 145 | Lucent 146 | 1.02 147 | 148 | 149 | MirOS Licence 150 | MirOS 151 | 152 | 153 | 154 | Microsoft Public License (Ms-PL) 155 | Ms-PL 156 | 157 | 158 | 159 | Microsoft Reciprocal License (Ms-RL) 160 | Ms-RL 161 | 162 | 163 | 164 | MIT license 165 | MIT 166 | 167 | 168 | 169 | Motosoto License 170 | Motosoto 171 | 172 | 173 | 174 | Mozilla Public License 1.1 (MPL) 175 | MPL 176 | 1.1 177 | 178 | 179 | Multics License 180 | Multics 181 | 182 | 183 | 184 | NASA Open Source Agreement 1.3 185 | NASA 186 | 1.3 187 | 188 | 189 | NTP License 190 | NTP 191 | 192 | 193 | 194 | Naumen Public License 195 | Naumen 196 | 197 | 198 | 199 | Nethack General Public License 200 | Nethack 201 | 202 | 203 | 204 | Nokia Open Source License 205 | Nokia 206 | 207 | 208 | 209 | Non-Profit Open Software License 3.0 (Non-Profit OSL 3.0) 210 | NP OSL 211 | 3.0 212 | 213 | 214 | OCLC Research Public License 2.0 215 | OSLC 216 | 2.0 217 | 218 | 219 | Open Font License 1.1 (OFL 1.1) 220 | OFL 221 | 1.1 222 | 223 | 224 | Open Group Test Suite License 225 | OGTSL 226 | 227 | 228 | 229 | Open Software License 3.0 (OSL 3.0) 230 | OSL 231 | 3.0 232 | 233 | 234 | PHP License 235 | PHP 236 | 237 | 238 | 239 | The PostgreSQL License 240 | PostgreSQL 241 | 242 | 243 | 244 | Python license (CNRI Python License) 245 | CNRI Python 246 | 247 | 248 | 249 | Python Software Foundation License 250 | Python 251 | 252 | 253 | 254 | Qt Public License (QPL) 255 | QPL 256 | 257 | 258 | 259 | RealNetworks Public Source License V1.0 260 | RealNW 261 | 1.0 262 | 263 | 264 | Reciprocal Public License 1.5 (RPL1.5) 265 | RPL 266 | 1.5 267 | 268 | 269 | Ricoh Source Code Public License 270 | Ricoh 271 | 272 | 273 | 274 | Simple Public License 2.0 275 | Simple 276 | 2.0 277 | 278 | 279 | Sleepycat License 280 | Sleepycat 281 | 282 | 283 | 284 | Sun Public License 285 | Sun 286 | 287 | 288 | 289 | Sybase Open Watcom Public License 1.0 290 | Sybase 291 | 1.0 292 | 293 | 294 | University of Illinois/NCSA Open Source License 295 | UI/NCSA 296 | 297 | 298 | 299 | Vovida Software License v. 1.0 300 | Vovida 301 | 1.0 302 | 303 | 304 | W3C License 305 | W3C 306 | 307 | 308 | 309 | wxWindows Library License 310 | wxWindows 311 | 312 | 313 | 314 | X.Net License 315 | X.Net 316 | 317 | 318 | 319 | Zope Public License 320 | Zope 321 | 322 | 323 | 324 | zlib/libpng license 325 | zlib/libpng 326 | 327 | 328 | 329 | BSL 1.0 330 | BSLv1 331 | 332 | 333 | BSL 1.0 334 | BSL1 335 | 336 | 337 | AFL 3.0 338 | AFL3 339 | 340 | 341 | AFL 3.0 342 | AFLv3 343 | 344 | 345 | Apache 2.0 346 | Apachev2 347 | 348 | 349 | Apache 2.0 350 | Apache2 351 | 352 | 353 | Artistic 2.0 354 | Artistic2 355 | 356 | 357 | Artistic 2.0 358 | Artisticv2 359 | 360 | 361 | CUAOPL 1.0 362 | CUAOPL1 363 | 364 | 365 | CUAOPL 1.0 366 | CUAOPLv1 367 | 368 | 369 | CPAL 1.0 370 | CPAL1 371 | 372 | 373 | CPAL 1.0 374 | CPALv1 375 | 376 | 377 | CATOSL 1.1 378 | CATOSL1 379 | 380 | 381 | CATOSL 1.1 382 | CATOSLv1 383 | 384 | 385 | ECL 2.0 386 | ECL2 387 | 388 | 389 | ECL 2.0 390 | ECLv2 391 | 392 | 393 | EFL 2.0 394 | EFL2 395 | 396 | 397 | EFL 2.0 398 | EFLv2 399 | 400 | 401 | GPL 3.0 402 | GPL3 403 | 404 | 405 | GPL 3.0 406 | GPLv3 407 | 408 | 409 | LGPL 3.0 410 | LGPL3 411 | 412 | 413 | LGPL 3.0 414 | LGPLv3 415 | 416 | 417 | Lucent 1.02 418 | Lucent1.02 419 | 420 | 421 | Lucent 1.02 422 | Lucentv1.02 423 | 424 | 425 | MPL 1.1 426 | MPL1.1 427 | 428 | 429 | MPL 1.1 430 | MPLv1.1 431 | 432 | 433 | NP OSL 3.0 434 | NPOSL3 435 | 436 | 437 | NP OSL 3.0 438 | NPOSLv3 439 | 440 | 441 | OSLC 2.0 442 | OSLC2 443 | 444 | 445 | OSLC 2.0 446 | OSLCv2 447 | 448 | 449 | OFL 1.1 450 | OFL1.1 451 | 452 | 453 | OFL 1.1 454 | OFLv1.1 455 | 456 | 457 | OSL 3.0 458 | OSL3 459 | 460 | 461 | OSL 3.0 462 | OSLv3 463 | 464 | 465 | RealNW 1.0 466 | Real1 467 | 468 | 469 | RealNW 1.0 470 | Realv1 471 | 472 | 473 | RPL 1.5 474 | RPL1.5 475 | 476 | 477 | RPL 1.5 478 | RPLv1.5 479 | 480 | 481 | Simple 2.0 482 | Simple2 483 | 484 | 485 | Simple 2.0 486 | Simplev2 487 | 488 | 489 | Sybase 1.0 490 | Sybase1 491 | 492 | 493 | Sybase 1.0 494 | Sybasev1 495 | 496 | 497 | Vovida 1.0 498 | Vovida1 499 | 500 | 501 | Vovida 1.0 502 | Vovidav1 503 | 504 | 505 | 506 | 507 | -------------------------------------------------------------------------------- /compliance/linkage/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.forms import ModelForm, forms, Form 3 | from django import forms 4 | import os 5 | import re 6 | 7 | # Create your models here. 8 | 9 | # setup some defaults/choices 10 | rchoices = tuple(enumerate(range(0, 100))) 11 | RECURSION_CHOICES = rchoices[1:] 12 | # fails under apache/wsgi 13 | try: 14 | DEFAULT_USER = os.environ['USER'] 15 | except: 16 | DEFAULT_USER = 'nobody' 17 | REL_CHOICES = (('Static','Static'), ('Dynamic','Dynamic'), ('Both', 'Both')) 18 | RANK_CHOICES = (('Low','Low'), ('Normal','Normal'), ('Critical', 'Critical')) 19 | STAT_CHOICES = (('A', 'Approve'), ('D','Disapprove')) 20 | 21 | def license_choices(): 22 | # get the available licenses to populated the form drop-downs 23 | licenses = License.objects.all().order_by('longname') 24 | # need a tuple for the drop-down 25 | choices = [] 26 | # no default 27 | choices.append(('','')) 28 | for lic in licenses: 29 | selector = lic.license 30 | if lic.version: 31 | selector += " " + lic.version 32 | choices.append((selector, selector)) 33 | 34 | return choices 35 | 36 | def library_choices(): 37 | # get the available libraries to populated the form drop-downs 38 | libraries = Lib.objects.exclude(library__contains='(static)').values('library').distinct() 39 | # need a tuple for the drop-down 40 | choices = [] 41 | # no default 42 | choices.append(('','')) 43 | for lib in libraries: 44 | selector = lib['library'] 45 | choices.append((selector, selector)) 46 | 47 | return choices 48 | 49 | def file_choices(): 50 | # get the available targets to populated the form drop-downs 51 | files = File.objects.filter(level = 1).values('file').distinct() 52 | # need a tuple for the drop-down 53 | choices = [] 54 | # no default 55 | choices.append(('','')) 56 | for f in files: 57 | choices.append((f['file'], f['file'])) 58 | 59 | return choices 60 | 61 | class Test(models.Model): 62 | do_search = models.BooleanField('Identify dependencies for a specific target file in a target directory', default=False) 63 | disable_static = models.BooleanField('Disable checking for static dependencies', default=False) 64 | test_done = models.BooleanField('Is the test complete?', default=False) 65 | recursion = models.IntegerField('Recursion level for dependency checking analysis', 66 | default = '1', choices = RECURSION_CHOICES) 67 | target = models.CharField('File/Path to run through the dependency check', max_length=200) 68 | target_dir = models.CharField('Target Directory', max_length=200, blank=True) 69 | test_date = models.DateTimeField('Test Date', auto_now=True) 70 | user = models.CharField('User Name (optional)', max_length=200, blank=True, default = DEFAULT_USER) 71 | project = models.CharField('Project Name (optional)', max_length=200, blank=True) 72 | comments = models.CharField('Comments (optional)', max_length=200, blank=True) 73 | def __unicode__(self): 74 | return self.target 75 | 76 | class File(models.Model): 77 | test = models.ForeignKey(Test) 78 | file = models.CharField(max_length=200) 79 | license = models.CharField(max_length=200, blank=True) 80 | level = models.IntegerField() 81 | parent_id = models.IntegerField() 82 | checked_static = models.BooleanField(default=False) 83 | def __unicode__(self): 84 | return self.file 85 | 86 | class Lib(models.Model): 87 | test = models.ForeignKey(Test) 88 | file = models.ForeignKey(File) 89 | library = models.CharField(max_length=200) 90 | static = models.BooleanField(default=False) 91 | license = models.CharField(max_length=200, blank=True) 92 | level = models.IntegerField() 93 | parent_id = models.IntegerField() 94 | def __unicode__(self): 95 | return self.library 96 | 97 | class License(models.Model): 98 | longname = models.CharField('License', max_length=200) 99 | license = models.CharField('License', max_length=200) 100 | version = models.CharField('Version', max_length=20, blank=True) 101 | def __unicode__(self): 102 | return self.license 103 | 104 | class Aliases(models.Model): 105 | license = models.CharField('License', max_length=200) 106 | alias = models.CharField('Alias', max_length=20, unique=True) 107 | def __unicode__(self): 108 | return self.license 109 | 110 | class LibLicense(models.Model): 111 | library = models.CharField('Library', max_length=200, unique=True) 112 | license = models.CharField('License', max_length=200) 113 | 114 | class FileLicense(models.Model): 115 | file = models.CharField('Library', max_length=200, unique=True) 116 | license = models.CharField('License', max_length=200) 117 | 118 | class Policy(models.Model): 119 | tlicense = models.CharField('Target License', max_length=200) 120 | dlicense = models.CharField('Dependency License', max_length=200) 121 | relationship = models.CharField('Relationship', max_length=20, 122 | default = 'Both', choices = REL_CHOICES) 123 | rank = models.CharField('Rank', max_length=20, 124 | default = 'Low', choices = RANK_CHOICES) 125 | status = models.CharField('Status', max_length=1, 126 | default = 'A', choices = STAT_CHOICES) 127 | edit_date = models.DateTimeField('test date', auto_now=True) 128 | def __unicode__(self): 129 | return self.tlicense 130 | 131 | class TestForm(ModelForm): 132 | class Meta: 133 | fields = "__all__" 134 | model = Test 135 | 136 | class FileForm(ModelForm): 137 | class Meta: 138 | fields = "__all__" 139 | model = File 140 | 141 | class LibForm(ModelForm): 142 | class Meta: 143 | fields = "__all__" 144 | model = Lib 145 | 146 | class LicenseForm(ModelForm): 147 | class Meta: 148 | fields = "__all__" 149 | model = License 150 | 151 | class PolicyForm(ModelForm): 152 | class Meta: 153 | fields = "__all__" 154 | model = Policy 155 | 156 | tlicense = forms.ChoiceField() 157 | dlicense = forms.ChoiceField() 158 | 159 | def __init__(self, *args, **kwargs): 160 | super(PolicyForm, self).__init__(*args, **kwargs) 161 | self.fields['tlicense'].choices = license_choices() 162 | self.fields['dlicense'].choices = license_choices() 163 | 164 | class LibLicenseForm(ModelForm): 165 | class Meta: 166 | fields = "__all__" 167 | model = LibLicense 168 | 169 | license = forms.ChoiceField() 170 | library = forms.ChoiceField() 171 | 172 | def __init__(self, *args, **kwargs): 173 | super(LibLicenseForm, self).__init__(*args, **kwargs) 174 | self.fields['license'].choices = license_choices() 175 | self.fields['library'].choices = library_choices() 176 | 177 | class FileLicenseForm(ModelForm): 178 | class Meta: 179 | fields = "__all__" 180 | model = FileLicense 181 | 182 | license = forms.ChoiceField() 183 | file = forms.ChoiceField() 184 | 185 | def __init__(self, *args, **kwargs): 186 | super(FileLicenseForm, self).__init__(*args, **kwargs) 187 | self.fields['license'].choices = license_choices() 188 | self.fields['file'].choices = file_choices() 189 | 190 | class AliasesForm(ModelForm): 191 | class Meta: 192 | fields = "__all__" 193 | model = Aliases 194 | 195 | license = forms.ChoiceField() 196 | 197 | def __init__(self, *args, **kwargs): 198 | super(AliasesForm, self).__init__(*args, **kwargs) 199 | self.fields['license'].choices = license_choices() 200 | 201 | class StaticSymbol(models.Model): 202 | symbol = models.CharField('Symbol', max_length=200, db_index=True) 203 | libraryname = models.CharField('Library', max_length=80) 204 | 205 | class StaticLibSearchPath(models.Model): 206 | path = models.CharField(max_length=100, primary_key=True) 207 | 208 | class SearchPathForm(Form): 209 | dirlist = forms.CharField(widget=forms.Textarea) 210 | 211 | def clean_dirlist(self): 212 | dl = self.cleaned_data['dirlist'] 213 | paths = dl.split("\n") 214 | for path in paths: 215 | if not os.path.isdir(path.strip()): 216 | raise forms.ValidationError('Path %s cannot be found.' 217 | % path.strip()) 218 | return dl 219 | 220 | class Meta(models.Model): 221 | name = models.CharField(max_length=32, db_index=True) 222 | value = models.CharField(max_length=128) 223 | 224 | def __unicode__(self): 225 | return "%s = %s" % (self.name, self.value) 226 | -------------------------------------------------------------------------------- /compliance/linkage/site_settings.py: -------------------------------------------------------------------------------- 1 | gui_name = "Dependency Checker Tool" 2 | gui_version = "1.0.1" 3 | # we have a field Rank in Policy, suppressed for now by this value 4 | show_rank = False 5 | -------------------------------------------------------------------------------- /compliance/linkage/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/linkage/templatetags/__init__.py -------------------------------------------------------------------------------- /compliance/linkage/templatetags/custom_tags.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | class AssignNode(template.Node): 4 | def __init__(self, name, value): 5 | self.name = name 6 | self.value = value 7 | 8 | def render(self, context): 9 | context[self.name] = self.value.resolve(context, True) 10 | return '' 11 | 12 | def do_assign(parser, token): 13 | """ 14 | Assign an expression to a variable in the current context. 15 | 16 | Syntax:: 17 | {% assign [name] [value] %} 18 | Example:: 19 | {% assign list entry.get_related %} 20 | 21 | """ 22 | bits = token.contents.split() 23 | if len(bits) != 3: 24 | raise template.TemplateSyntaxError("'%s' tag takes two arguments" % bits[0]) 25 | value = parser.compile_filter(bits[2]) 26 | return AssignNode(bits[1], value) 27 | 28 | register = template.Library() 29 | register.tag('assign', do_assign) -------------------------------------------------------------------------------- /compliance/linkage/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /compliance/linkage/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | from django.template import Context, loader 3 | from django.shortcuts import render_to_response, get_object_or_404 4 | from compliance.linkage.models import Test, File, Lib, License, Aliases, LibLicense, \ 5 | FileLicense, Policy, TestForm, LicenseForm, PolicyForm, \ 6 | LibLicenseForm, FileLicenseForm, AliasesForm, \ 7 | StaticSymbol, StaticLibSearchPath, SearchPathForm 8 | from django.http import HttpResponse, HttpResponseRedirect 9 | from django.http import Http404 10 | from django.conf import settings 11 | from django.db.models import Q 12 | 13 | from compliance import load_static, task 14 | 15 | import sys, os, re, urllib, subprocess, time 16 | 17 | # used for binding and policy 18 | is_static = '(static)' 19 | # buffer size for Popen, we want unbuffered 20 | bufsize = -1 21 | 22 | ### each of these views has a corresponding html page in ../templates/linkage 23 | 24 | # task status page - intended for calling in javascript 25 | def taskstatus(request): 26 | tm = task.TaskManager() 27 | return HttpResponse(tm.read_status()) 28 | 29 | # main page 30 | def index(request): 31 | from site_settings import gui_name, gui_version 32 | return render_to_response('linkage/index.html', {'name': gui_name, 'version': gui_version}) 33 | 34 | # test run detail page 35 | def detail(request, test_id): 36 | t, masterlist = render_detail(test_id) 37 | return render_to_response('linkage/detail.html', {'test': t, 'master': masterlist, 'tab_results': True}) 38 | 39 | # results list page - this is also a form, for test deletions 40 | def results(request): 41 | if request.method == 'POST': # If the form has been submitted... 42 | testlist = request.POST.get('testlist', '') 43 | if testlist != '': 44 | tests = testlist.split(",") 45 | 46 | # delete all the selected tests from the database 47 | for test in tests: 48 | if test != '': 49 | delete_test_record(test) 50 | 51 | latest_test_list = Test.objects.filter(test_done = True).order_by('-test_date') 52 | return render_to_response('linkage/results.html', {'latest_test_list': latest_test_list, 'tab_results': True}) 53 | 54 | # licenses entry/maintenance page 55 | def licenses(request): 56 | errmsg = '' 57 | if request.method == 'POST': # If the form has been submitted... 58 | mode = urllib.unquote(request.POST.get('submit')) 59 | 60 | if re.search("^Add", mode) and re.search("License", mode): 61 | licenseform = LicenseForm(request.POST) # A form bound to the POST data 62 | # request to add data 63 | if licenseform.is_valid(): # All validation rules pass 64 | licenseform.save() 65 | 66 | if re.search("^Add", mode) and re.search("Aliases", mode): 67 | aliasesform = AliasesForm(request.POST) # A form bound to the POST data 68 | # request to add data - we may have multiple aliases to add 69 | if aliasesform.is_valid(): # All validation rules pass 70 | license = aliasesform.cleaned_data['license'] 71 | errlist = [] 72 | for i in range(1,10): 73 | ainput = request.POST.get('alinput' + str(i), '') 74 | if ainput: 75 | aliasdata = Aliases(license = license, alias = ainput) 76 | try: 77 | aliasdata.save() 78 | except: 79 | errlist.append(str(ainput)) 80 | 81 | if errlist: 82 | errmsg = "Warning: failed to add duplicate aliases " + str(errlist) 83 | 84 | if re.search("^Delete Selected Licenses", mode): 85 | # delete request 86 | licenselist = request.POST.get('licenselist', '') 87 | if licenselist != '': 88 | delete_records(License, licenselist) 89 | 90 | if re.search("^Delete Selected Aliases", mode): 91 | # delete request 92 | aliaslist = request.POST.get('aliaslist', '') 93 | if aliaslist != '': 94 | # not by id here, so don't call delete_records 95 | records = aliaslist.split(",") 96 | 97 | for record in records: 98 | if record != '': 99 | q = Aliases.objects.filter(license = record) 100 | q.delete() 101 | 102 | licenseform = LicenseForm() # An unbound form 103 | aliasesform = AliasesForm() # An unbound form 104 | 105 | latest_license_list = License.objects.all().order_by('longname') 106 | # we represent this one differently in the gui, pre-arrange things here 107 | aliases_list = Aliases.objects.values('license').distinct() 108 | 109 | for l in aliases_list: 110 | alias_list = Aliases.objects.values('alias').filter(license = l['license']) 111 | aliases = '' 112 | for a in alias_list: 113 | aliases += a['alias'] + ' | ' 114 | # chomp the last "or" off 115 | l['alias'] = aliases[:-3] 116 | 117 | # we want multiple input boxes to enter a number of aliases per license, at once 118 | al_input = [] 119 | for i in range(1,10): 120 | al_input.append('') 121 | 122 | return render_to_response('linkage/licenses.html', { 123 | 'errmsg': errmsg, 124 | 'latest_license_list': latest_license_list, 125 | 'latest_aliases_list': aliases_list, 126 | 'licenseform': licenseform, 127 | 'aliasesform': aliasesform, 128 | 'input_list': al_input, 129 | 'tab_licenses': True }) 130 | 131 | # policy list page - this is also a form, for policy deletions/updates 132 | def policy(request): 133 | from site_settings import show_rank 134 | 135 | if request.method == 'POST': # If the form has been submitted... 136 | mode = urllib.unquote(request.POST.get('submit')) 137 | 138 | if re.search("^Add", mode): 139 | policyform = PolicyForm(request.POST) # A form bound to the POST data 140 | # request to add data 141 | if policyform.is_valid(): # All validation rules pass 142 | policyform.save() 143 | 144 | else: 145 | # delete request 146 | policylist = request.POST.get('policylist', '') 147 | if policylist != '': 148 | delete_records(Policy, policylist) 149 | 150 | policyform = PolicyForm() # An unbound form 151 | 152 | latest_policy_list = Policy.objects.all().order_by('-edit_date') 153 | 154 | return render_to_response('linkage/policy.html', { 155 | 'show_rank': show_rank, 156 | 'latest_policy_list': latest_policy_list, 157 | 'policyform': policyform, 158 | 'tab_policy': True }) 159 | 160 | # target/library/license binding page 161 | def lbindings(request): 162 | 163 | if request.method == 'POST': # If the form has been submitted... 164 | mode = urllib.unquote(request.POST.get('submit')) 165 | 166 | if re.search("^Add Target", mode): 167 | targetlicenseform = FileLicenseForm(request.POST) # A form bound to the POST data 168 | # request to add data 169 | if targetlicenseform.is_valid(): # All validation rules pass 170 | targetlicenseform.save() 171 | 172 | if re.search("^Add Library", mode): 173 | liblicenseform = LibLicenseForm(request.POST) # A form bound to the POST data 174 | # request to add data 175 | if liblicenseform.is_valid(): # All validation rules pass 176 | liblicenseform.save() 177 | 178 | if re.search("^Update Target", mode): 179 | update_file_bindings() 180 | 181 | if re.search("^Update Library", mode): 182 | update_lib_bindings() 183 | 184 | if re.search("^Delete Selected", mode) and re.search("Target Bindings", mode): 185 | # delete request 186 | targetlicenselist = request.POST.get('targetlicenselist', '') 187 | if targetlicenselist != '': 188 | delete_records(FileLicense, targetlicenselist) 189 | 190 | if re.search("^Delete Selected", mode) and re.search("Library Bindings", mode): 191 | # delete request 192 | liblicenselist = request.POST.get('liblicenselist', '') 193 | if liblicenselist != '': 194 | delete_records(LibLicense, liblicenselist) 195 | 196 | targetlicenseform = FileLicenseForm() # An unbound form 197 | liblicenseform = LibLicenseForm() # An unbound form 198 | 199 | latest_targetlicense_list = FileLicense.objects.all().order_by('file') 200 | latest_liblicense_list = LibLicense.objects.all().order_by('library') 201 | 202 | return render_to_response('linkage/lbindings.html', { 203 | 'latest_targetlicense_list': latest_targetlicense_list, 204 | 'latest_liblicense_list': latest_liblicense_list, 205 | 'targetlicenseform': targetlicenseform, 206 | 'liblicenseform': liblicenseform, 207 | 'tab_lbindings': True }) 208 | 209 | # settings - miscellaneous things to set, used for static db reloading 210 | def settings_form(request): 211 | 212 | # This is the task to be executed. 213 | def static_reload_task(): 214 | lib_list = load_static.get_library_list() 215 | sys.stdout.write("JOBDESC: Reloading static symbol data.\n") 216 | sys.stdout.write("COUNT: %d\n" % len(lib_list)) 217 | sys.stdout.write("MESSAGE: Deleting old data...\n") 218 | sys.stdout.flush() 219 | StaticSymbol.objects.all().delete() 220 | for lib in lib_list: 221 | sys.stdout.write("ITEM: " + lib + "\n") 222 | sys.stdout.flush() 223 | load_static.load_symbols(lib) 224 | load_static.set_last_update_date() 225 | 226 | infomsg = None 227 | tm = task.TaskManager() 228 | 229 | if request.method == 'POST': 230 | if 'reload_static' in request.POST: 231 | if not tm.is_running(): 232 | tm.start(static_reload_task) 233 | return HttpResponseRedirect('/linkage/settings/') 234 | else: 235 | infomsg = "The static database is already being reloaded." 236 | elif 'change_search_paths' in request.POST: 237 | search_path_form = SearchPathForm(request.POST) 238 | if search_path_form.is_valid(): 239 | path_list = search_path_form.cleaned_data['dirlist'].split("\n") 240 | path_list = [x.strip() for x in path_list] 241 | StaticLibSearchPath.objects.all().delete() 242 | for path_str in path_list: 243 | path = StaticLibSearchPath(path=path_str) 244 | path.save() 245 | else: 246 | infomsg = "One of the directory paths could not be found." 247 | else: 248 | infomsg = "Could not understand the form request." 249 | 250 | # Load the current search paths, and create the form. 251 | search_paths = [x.path for x in StaticLibSearchPath.objects.all()] 252 | search_path_str = "\n".join(search_paths) 253 | search_path_form = SearchPathForm({ 'dirlist': search_path_str }) 254 | 255 | # Warn the user if static data is missing and no other info 256 | # is being presented. 257 | if not infomsg and not tm.is_running() and \ 258 | StaticSymbol.objects.all().count() == 0: 259 | infomsg = "No data for static symbols is present. Please use the Reload button to load this data." 260 | 261 | return render_to_response('linkage/settings.html', 262 | { 'info_message': infomsg, 263 | 'tab_settings': True, 264 | 'last_staticdb_update': load_static.get_last_update_date(), 265 | 'reload_running': tm.is_running(), 266 | 'search_path_form': search_path_form }) 267 | 268 | # process test form - this is where the real work happens 269 | def test(request): 270 | cli_command = settings.CLI_COMMAND + " -c" 271 | 272 | # This is the task to be executed. 273 | def get_deps_task(): 274 | sys.stdout.write("JOBDESC: Running " + cli_command + ".\n") 275 | sys.stdout.flush() 276 | # run the back end with given parameters and push the data into the database 277 | errmsg = None 278 | lastfile = '' 279 | parentid = 0 280 | libparentid = 0 281 | sys.stdout.write("MSGADD: Collecting dependencies...\n") 282 | sys.stdout.flush() 283 | proc = subprocess.Popen(cli_command.split(), bufsize=bufsize, stdout=subprocess.PIPE) 284 | for data in iter(proc.stdout.readline,''): 285 | errmsg, lastfile, parentid, libparentid = process_results(data, testid, lastfile, parentid, libparentid) 286 | # if we got an error, delete the test entry 287 | if errmsg: 288 | delete_test_record(testid) 289 | sys.stdout.write("MSGADD: Error: " + errmsg + "\n") 290 | sys.stdout.flush() 291 | time.sleep(30) 292 | return 293 | 294 | try: 295 | (rlevel, item) = data.strip().split(",")[:2] 296 | sys.stdout.write("MSGADD: %s (%s)\n" % (item, rlevel)) 297 | except: 298 | sys.stdout.write("MSGADD: " + data) 299 | sys.stdout.flush() 300 | 301 | if not errmsg: 302 | mark_test_done(testid) 303 | update_license_bindings() 304 | sys.stdout.write("MSGADD: Test Id=" + str(testid) + "\n") 305 | sys.stdout.flush() 306 | sys.stdout.write('MSGADD: Test Complete, click here to view results\n') 307 | sys.stdout.flush() 308 | # FIXME - the redirect doesn't happen without a delay here 309 | time.sleep(5) 310 | 311 | infomsg = None 312 | tm = task.TaskManager() 313 | 314 | if request.method == 'POST': # If the form has been submitted... 315 | testform = TestForm(request.POST) # A form bound to the POST data 316 | if testform.is_valid(): # All validation rules pass 317 | target = testform.cleaned_data['target'] 318 | disable_static = testform.cleaned_data['disable_static'] 319 | if disable_static: 320 | cli_command += " --no-static " 321 | do_search = testform.cleaned_data['do_search'] 322 | if do_search: 323 | target_dir = testform.cleaned_data['target_dir'] 324 | cli_command += " -s " + target_dir 325 | cli_command += " " + target 326 | recursion = testform.cleaned_data['recursion'] 327 | cli_command += " " + str(recursion) 328 | # form doesn't have the id, but we can get the db model and then get it 329 | testdata = testform.save(commit=False) 330 | testdata.save() 331 | testid = testdata.id 332 | 333 | if not tm.is_running(): 334 | tm.start(get_deps_task) 335 | return HttpResponseRedirect('/linkage/test/') 336 | 337 | else: 338 | testform = TestForm() # An unbound form 339 | 340 | return render_to_response('linkage/test.html', { 341 | 'info_message': infomsg, 342 | 'testform': testform, 343 | 'tab_test': True, 344 | 'reload_running': tm.is_running(), 345 | 'static_data': StaticSymbol.objects.all().count() > 0, 346 | }) 347 | 348 | ### these are all basically documentation support 349 | 350 | # doc page 351 | def documentation(request): 352 | from site_settings import gui_name, gui_version 353 | 354 | # Read the standalone docs, and reformat for the gui 355 | docs = '' 356 | status = 0 357 | 358 | try: 359 | f = open(settings.STATIC_DOC_ROOT + "/docs/index.html", 'r') 360 | 361 | except: 362 | # docs are created yet, try to do it 363 | status = os.system("cd " + settings.STATIC_DOC_ROOT + "/docs && make") 364 | if status != 0: 365 | status = os.system("cd " + settings.STATIC_DOC_ROOT + "/docs && ./text-docs-to-html > index.html.addons") 366 | if status == 0: 367 | status = os.system("cd " + settings.STATIC_DOC_ROOT + "/docs && cat index.html.base index.html.addons index.html.footer > index.html") 368 | else: 369 | docs = "Error, no index.html in compliance/media/docs.
" 370 | docs += "If working with a git checkout or tarball, please type 'make' in the top level directory.
" 371 | docs += "" 372 | 373 | # something worked above 374 | if not docs: 375 | f = open(settings.STATIC_DOC_ROOT + "/docs/index.html", 'r') 376 | doc_index = [] 377 | for line in f: 378 | #replace the div styles for embedded use 379 | line = line.replace('
', '
') 380 | line = line.replace('
', '
') 381 | line = line.replace(''] 400 | try: 401 | d=urllib.unquote(request.POST.get('dir')) 402 | content = os.listdir(d) 403 | # slows things a little, but looks more like 'ls' 404 | for f in sorted(content, key=unicode.lower): 405 | ff=os.path.join(d,f) 406 | if ff not in not_wanted and f != 'lost+found': 407 | if os.path.isdir(ff): 408 | r.append('' % (ff,f)) 409 | else: 410 | e=os.path.splitext(f)[1][1:] # get .ext and remove dot 411 | r.append('
  • %s
  • ' % (e,ff,f)) 412 | r.append('') 413 | except Exception,e: 414 | r.append('Could not load directory: %s' % str(e)) 415 | r.append('') 416 | return HttpResponse(''.join(r)) 417 | 418 | ### utility functions 419 | 420 | # open up a test record (used for automated testing) 421 | def create_test_record(do_search, disable_static, recursion, target, target_dir, user, project, comments): 422 | testdata = Test(do_search = do_search, disable_static = disable_static, 423 | recursion = recursion, target = target, target_dir = target_dir, 424 | user = user, project = project, comments = comments) 425 | testdata.save() 426 | return testdata.id 427 | 428 | # mark a test as done 429 | def mark_test_done(testid): 430 | Test.objects.filter(id = testid).update(test_done = True) 431 | 432 | # to remove a test record 433 | def delete_test_record(testid): 434 | q = Test.objects.filter(id = testid) 435 | q.delete() 436 | # seems django automagically cleans File, Lib due to the foreign key 437 | 438 | # we have the data, either from the cli or gui, process and load if ok 439 | def process_results(dbdata, testid, lastfile, parentid, parentlibid): 440 | errmsg = '' 441 | t = get_object_or_404(Test, pk=testid) 442 | if not re.search("(does not|was not found|not an ELF)", dbdata): 443 | dbdata = dbdata.rstrip("\r\n") 444 | # format for level 1 is: depth, parent, dep 445 | # format for level 1 + N is: depth, child path, child, dep, dep... 446 | deps = dbdata.split(",") 447 | 448 | # write the file record 449 | depth = int(deps[0]) 450 | testfile = deps[1] 451 | # the top level file may show multiple times, only get the first one 452 | #filedata = File(test_id = testid, file = testfile, level = depth, parent_id = 0) 453 | if depth == 1 and testfile != lastfile: 454 | filedata = File(test_id = testid, file = testfile, level = depth, parent_id = 0) 455 | filedata.save() 456 | parentid = filedata.id 457 | lastfile = testfile 458 | filedata.parent_id = parentid 459 | filedata.save() 460 | elif depth != 1: 461 | # FIXME - right now we're not really doing anything with these 462 | # the 'child' gets the parent's id 463 | filedata = File(test_id = testid, file = testfile, level = depth, parent_id = parentid) 464 | filedata.save() 465 | fileid = filedata.id 466 | 467 | # now the lib records 468 | offset = 2 469 | # child records have the lib path and the parent dep 470 | if depth > 1: 471 | offset = 3 472 | checked_static = not t.disable_static 473 | for lib in deps[offset:len(deps)]: 474 | if re.search(r'WARNING: Could not check', lib): 475 | checked_static = False 476 | continue 477 | static = False 478 | if re.search(is_static, lib): 479 | lib = lib.split()[0] 480 | static = True 481 | # we link file_id to parent_id of the file for recursion 482 | libdata = Lib(test_id = testid, file_id = parentid, library = lib, static = static, level = depth, parent_id = 0) 483 | libdata.save() 484 | libid = libdata.id 485 | 486 | # the 'child' libs get the parent's id 487 | if depth == 1: 488 | parentlibid = libid 489 | libdata.parent_id = parentlibid 490 | libdata.save() 491 | 492 | # FIXME - before I added the if, I got 4 target entries in the results for a recursive scan 493 | # (if I move filepath= out of the if above), or an error 494 | if depth == 1 and testfile != lastfile: 495 | # save static status 496 | filedata.checked_static = checked_static 497 | filedata.save() 498 | 499 | else: 500 | # do feedback in the gui from here 501 | errmsg += dbdata 502 | 503 | return errmsg, lastfile, parentid, parentlibid 504 | 505 | # delete table records requested by id from one of the input forms 506 | def delete_records(table, rlist): 507 | 508 | records = rlist.split(",") 509 | 510 | for record in records: 511 | if record != '': 512 | q = table.objects.filter(id = record) 513 | q.delete() 514 | 515 | # update both file and library bindings 516 | def update_license_bindings(): 517 | update_lib_bindings() 518 | update_file_bindings() 519 | 520 | # update Lib records for license bindings 521 | def update_lib_bindings(): 522 | llist = LibLicense.objects.all().order_by('library') 523 | # bind both the dynamic and static version 524 | for ll in llist: 525 | Lib.objects.filter(library = ll.library).update(license = ll.license) 526 | 527 | # update File records for license bindings 528 | def update_file_bindings(): 529 | flist = FileLicense.objects.all().order_by('file') 530 | for fl in flist: 531 | File.objects.filter(file = fl.file).update(license = fl.license) 532 | 533 | # list of aliases for a license 534 | def get_license_aliases(license): 535 | alias_list = Aliases.objects.values('alias').filter(license = license) 536 | alist = [] 537 | for a in alias_list: 538 | alist.append(a['alias']) 539 | return alist 540 | 541 | # check a target/library pair for policy violations 542 | def check_policy(flicense, llicense, static, issue): 543 | # is the lib dynamic or static? 544 | ltype = 'Dynamic' 545 | if static: 546 | ltype = 'Static' 547 | 548 | # it's possible that the license assigned to the target or library is one of 549 | # the aliases, in which case we need the 'official' name for the policy check 550 | pllicense = llicense # we want to display both names in the report, if present 551 | pflicense = flicense 552 | 553 | llicenseset = Aliases.objects.filter(alias = llicense) 554 | if llicenseset: 555 | # can only be one match 556 | pllicense = llicenseset[0].license 557 | if llicense != pllicense: 558 | # plug in the alias (real name) 559 | llicense = llicense + ' (' + pllicense + ')' 560 | 561 | flicenseset = Aliases.objects.filter(alias = flicense) 562 | if flicenseset: 563 | # can only be one match 564 | pflicense = flicenseset[0].license 565 | if flicense != pflicense: 566 | flicense = flicense + ' (' + pflicense + ')' 567 | 568 | policyset = Policy.objects.filter(tlicense = pflicense, dlicense = pllicense) 569 | policyset = policyset.filter(Q(relationship = ltype) | Q(relationship = 'Both')) 570 | # if we got multiple matches, just return - bad policies 571 | if policyset and policyset.count() < 2: 572 | status = policyset[0].status 573 | # only set the issue flag for the target coloring for the disallowed case 574 | if status == 'D': 575 | issue = issue or True 576 | llicense = flag_policy_issue(llicense, status) 577 | 578 | # highlight if there is no policy defined 579 | if not policyset: 580 | llicense = flag_policy_issue(llicense, 'U') 581 | 582 | return llicense, issue 583 | 584 | # flag a policy issue for the test results rendering 585 | def flag_policy_issue(value, status): 586 | # to highlight the issues 587 | tag_start = '' 589 | tag_end = '' 590 | tcolor = '' 591 | if status == 'U': 592 | tcolor = 'orange' 593 | tag_end += 'orange_flag.png' 594 | if status == 'D': 595 | tcolor = 'red' 596 | tag_end += 'red_flag.png' 597 | value = tag_start + tcolor + tag_mid + value + tag_end 598 | return value 599 | 600 | # pre-render the table data for the detail page 601 | def render_detail(test_id): 602 | t = get_object_or_404(Test, pk=test_id) 603 | # update any new bindings 604 | update_license_bindings() 605 | # all we want is the level 1 files 606 | fileset = t.file_set.filter(level = 1) 607 | libset = t.lib_set.all() 608 | 609 | # the table renders too slow walking through the one-to-many of 610 | # files -> libs, prefill a list with file, license, libs, lib_license 611 | # and indent the recursion level, flag polices here, template just blobs out the table 612 | 613 | TBD = 'TBD' 614 | static_warning = 'Not available' 615 | spacer = "  " 616 | masterlist = [] 617 | 618 | for file in fileset: 619 | flicense = TBD 620 | policy_issue = False 621 | if file.license: 622 | flicense = file.license 623 | liblist = '' 624 | liclist = '' 625 | newline = '' 626 | if not t.disable_static and not file.checked_static: 627 | staticlist = static_warning 628 | else: 629 | staticlist = '' 630 | # old way we only hit the database once, but things got messy tracking 631 | # this doesn't seem to bog things down too much 632 | for lib in libset.filter(file = file.id): 633 | # no indent for level 1 634 | level = lib.level - 1 635 | liblist += newline + spacer * level + lib.library 636 | if t.disable_static or file.checked_static: 637 | staticlist += newline 638 | if lib.static: 639 | staticlist += 'x' 640 | llicense = TBD 641 | if lib.license: 642 | # don't worry about policy if we don't have both licenses 643 | if file.license: 644 | llicense, policy_issue = check_policy(file.license, lib.license, lib.static, policy_issue) 645 | else: 646 | llicense = lib.license 647 | liclist += newline + llicense 648 | newline = '
    ' 649 | 650 | # flag the target license if there was any issue 651 | if policy_issue: 652 | flicense = flag_policy_issue(flicense, 'D') 653 | 654 | masterlist.append({'file': file.file, 'license': flicense, 'libs': liblist, 'statics': staticlist, 'licenses': liclist}) 655 | 656 | return t, masterlist 657 | 658 | -------------------------------------------------------------------------------- /compliance/load_static.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # load_static.py - load static data into the database. 4 | # Copyright 2010 Linux Foundation. 5 | # Jeff Licquia 6 | 7 | import os 8 | if __name__ == "__main__": 9 | os.environ["DJANGO_SETTINGS_MODULE"] = "compliance.settings" 10 | 11 | import sys 12 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 13 | 14 | import re 15 | import datetime 16 | 17 | from django.db import transaction 18 | from django.core import exceptions 19 | 20 | from compliance.linkage.models import StaticSymbol, StaticLibSearchPath, Meta 21 | 22 | def get_library_list(): 23 | lib_list = [] 24 | search_paths = [x.path for x in StaticLibSearchPath.objects.all()] 25 | 26 | # Always load .so.[number] files (dynamic symbols) 27 | for libpath in search_paths: 28 | if os.path.isdir(libpath): 29 | for libfile in os.listdir(libpath): 30 | if re.search(r'\.so\.\d+$', libfile): 31 | lib_list.append(os.path.join(libpath, libfile)) 32 | 33 | # For .a files, only load them if a .so.[number] isn't already 34 | # being loaded. 35 | for libpath in search_paths: 36 | if os.path.isdir(libpath): 37 | for libfile in os.listdir(libpath): 38 | if re.search(r'\.a$', libfile): 39 | solibfile = libfile.replace('.a', '.so') 40 | solibpath = os.path.join(libpath, solibfile) 41 | 42 | # Symlinked .so files. 43 | if os.path.islink(solibpath): 44 | dynlibpath = os.readlink(solibpath) 45 | if dynlibpath in lib_list: 46 | continue 47 | 48 | # ld scripts. 49 | elif os.path.exists(solibpath): 50 | libtype = os.popen("file " + solibpath).read() 51 | if libtype.find("ASCII"): 52 | found_so = False 53 | solib = open(solibpath) 54 | for line in solib: 55 | match = re.search(r'GROUP\s*\((.+)\)\s*$', line) 56 | if match: 57 | for item in match.group(1).strip().split(): 58 | if os.path.exists(item) and \ 59 | item in lib_list: 60 | found_so = True 61 | break 62 | if found_so: 63 | continue 64 | 65 | lib_list.append(os.path.join(libpath, libfile)) 66 | 67 | return lib_list 68 | 69 | def get_symbols(lib_fn): 70 | sym_list = [] 71 | if re.search(r'\.a$', lib_fn): 72 | objdump = os.popen("objdump -t " + lib_fn) 73 | for line in objdump: 74 | if not re.search(r'^\d+', line): 75 | continue 76 | items = line.strip().split() 77 | if "F" in items: 78 | sym_list.append(items[-1]) 79 | else: 80 | objdump = os.popen("objdump -T " + lib_fn) 81 | for line in objdump: 82 | if line.find(".text") < 0: 83 | continue 84 | sym_list.append(line.strip().split()[-1]) 85 | 86 | return sym_list 87 | 88 | @transaction.atomic 89 | def set_last_update_date(): 90 | try: 91 | last_update = Meta.objects.get(name="last_staticdb_update") 92 | last_update.value = str(datetime.date.today()) 93 | except exceptions.ObjectDoesNotExist: 94 | last_update = Meta(name="last_staticdb_update", 95 | value=str(datetime.date.today())) 96 | 97 | last_update.save() 98 | 99 | def get_last_update_date(): 100 | try: 101 | return Meta.objects.get(name="last_staticdb_update").value 102 | except (Meta.DoesNotExist, exceptions.ObjectDoesNotExist): 103 | return None 104 | 105 | @transaction.atomic 106 | def load_symbols(lib_fn): 107 | lib_name = os.path.basename(lib_fn) 108 | for symbol in get_symbols(lib_fn): 109 | sym_db = StaticSymbol(symbol=symbol, libraryname=lib_name) 110 | sym_db.save() 111 | 112 | def main(): 113 | sys.stdout.write("Clearing out old data...") 114 | sys.stdout.flush() 115 | 116 | try: 117 | StaticSymbol.objects.all().delete() 118 | except: 119 | transaction.rollback() 120 | sys.stdout.write("\n") 121 | raise 122 | else: 123 | transaction.commit() 124 | sys.stdout.write("\n") 125 | 126 | for lib in get_library_list(): 127 | sys.stdout.write("Loading symbols from " + lib + "...") 128 | sys.stdout.flush() 129 | load_symbols(lib) 130 | sys.stdout.write("\r") 131 | sys.stdout.write(" " * 79) 132 | sys.stdout.write("\r") 133 | sys.stdout.flush() 134 | set_last_update_date() 135 | 136 | if __name__ == "__main__": 137 | main() 138 | -------------------------------------------------------------------------------- /compliance/media/css/barstyle.css: -------------------------------------------------------------------------------- 1 | div.bar { 2 | display: block; 3 | float: left; 4 | width: 500px; 5 | height: 12px; 6 | margin: 0 0 2px; 7 | border: 2px solid gray; 8 | background: #fff; 9 | } 10 | 11 | div.bar div.completed { 12 | display: block; 13 | float: left; 14 | width: 50%; 15 | height: 12px; 16 | text-align: right; 17 | background: #00f; 18 | } 19 | -------------------------------------------------------------------------------- /compliance/media/css/bodystyle.css: -------------------------------------------------------------------------------- 1 | #lmargin { 2 | float: left; 3 | width: 1%; 4 | } 5 | #main { 6 | float: left; 7 | width: 98%; 8 | } 9 | #rmargin { 10 | float: left; 11 | width: 1%; 12 | } 13 | -------------------------------------------------------------------------------- /compliance/media/css/containers.css: -------------------------------------------------------------------------------- 1 | .container { 2 | float: left; 3 | margin: 5px; 4 | padding: 5px; 5 | } 6 | 7 | .container select { 8 | width: 95%; 9 | } 10 | 11 | .cfooter { 12 | clear: both; 13 | margin: 20px; 14 | padding: 5px; 15 | } 16 | 17 | .browser { 18 | width: 220px; 19 | height: 300px; 20 | border-top: solid 1px #BBB; 21 | border-left: solid 1px #BBB; 22 | border-bottom: solid 1px #FFF; 23 | border-right: solid 1px #FFF; 24 | background: #FFF; 25 | overflow: scroll; 26 | padding: 5px; 27 | } 28 | 29 | .executor { 30 | width: 600px; 31 | height: 300px; 32 | border-top: solid 1px #BBB; 33 | border-left: solid 1px #BBB; 34 | border-bottom: solid 1px #FFF; 35 | border-right: solid 1px #FFF; 36 | background: #FFF; 37 | overflow: scroll; 38 | padding: 5px; 39 | } 40 | -------------------------------------------------------------------------------- /compliance/media/css/docstyle.css: -------------------------------------------------------------------------------- 1 | #lside { 2 | position: fixed; 3 | width: 14%; 4 | height: 100%; 5 | top: 0%; 6 | left: 0; 7 | margin: 5; 8 | background:#DAE0D2; 9 | font-size: smaller; 10 | } 11 | #lside ul { 12 | margin: 0; 13 | padding-left: 5px; 14 | padding-right: 0px; 15 | list-style-type: none; 16 | } 17 | /* same as lside, but offset 22% down for embedded in the gui, don't shrink fonts */ 18 | #lside_e { 19 | position: fixed; 20 | width: 14%; 21 | height: 78%; 22 | top: 22%; 23 | left: 0; 24 | margin: 5; 25 | background:#DAE0D2; 26 | } 27 | #lside_e ul { 28 | margin: 0; 29 | padding-left: 5px; 30 | padding-right: 0px; 31 | list-style-type: none; 32 | } 33 | #main { 34 | position: fixed; 35 | width: 85%; 36 | height: 100%; 37 | top: 0%; 38 | left: 14%; 39 | margin: 5; 40 | padding-left: 10px; 41 | font-size: smaller; 42 | overflow: auto; 43 | } 44 | /* same as main, but offset 22% down for embedded in the gui, don't shrink fonts */ 45 | #main_e { 46 | position: fixed; 47 | width: 85%; 48 | height: 78%; 49 | top: 22%; 50 | left: 14%; 51 | margin: 5; 52 | padding-left: 10px; 53 | overflow: auto; 54 | } 55 | #rside { 56 | position: fixed; 57 | width: 1%; 58 | height: 100%; 59 | top: 0%; 60 | left: 99%; 61 | } 62 | #main iframe { 63 | width: 100%; 64 | height: 100%; 65 | scrolling: no; 66 | border: 0; 67 | } 68 | -------------------------------------------------------------------------------- /compliance/media/css/jqueryFileTree.css: -------------------------------------------------------------------------------- 1 | UL.jqueryFileTree { 2 | font-family: Verdana, sans-serif; 3 | font-size: 11px; 4 | line-height: 18px; 5 | padding: 0px; 6 | margin: 0px; 7 | } 8 | 9 | UL.jqueryFileTree LI { 10 | list-style: none; 11 | padding: 0px; 12 | padding-left: 20px; 13 | margin: 0px; 14 | white-space: nowrap; 15 | } 16 | 17 | UL.jqueryFileTree A { 18 | color: #333; 19 | text-decoration: none; 20 | display: block; 21 | padding: 0px 2px; 22 | } 23 | 24 | UL.jqueryFileTree A:hover { 25 | background: #BDF; 26 | } 27 | 28 | /* Core Styles */ 29 | .jqueryFileTree LI.directory { background: url(/site_media/images/filetree/directory.png) left top no-repeat; } 30 | .jqueryFileTree LI.expanded { background: url(/site_media/images/filetree/folder_open.png) left top no-repeat; } 31 | .jqueryFileTree LI.file { background: url(/site_media/images/filetree/file.png) left top no-repeat; } 32 | .jqueryFileTree LI.wait { background: url(/site_media/images/filetree/spinner.gif) left top no-repeat; } 33 | /* File Extensions*/ 34 | .jqueryFileTree LI.ext_3gp { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 35 | .jqueryFileTree LI.ext_afp { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 36 | .jqueryFileTree LI.ext_afpa { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 37 | .jqueryFileTree LI.ext_asp { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 38 | .jqueryFileTree LI.ext_aspx { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 39 | .jqueryFileTree LI.ext_avi { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 40 | .jqueryFileTree LI.ext_bat { background: url(/site_media/images/filetree/application.png) left top no-repeat; } 41 | .jqueryFileTree LI.ext_bmp { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 42 | .jqueryFileTree LI.ext_c { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 43 | .jqueryFileTree LI.ext_cfm { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 44 | .jqueryFileTree LI.ext_cgi { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 45 | .jqueryFileTree LI.ext_com { background: url(/site_media/images/filetree/application.png) left top no-repeat; } 46 | .jqueryFileTree LI.ext_cpp { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 47 | .jqueryFileTree LI.ext_css { background: url(/site_media/images/filetree/css.png) left top no-repeat; } 48 | .jqueryFileTree LI.ext_doc { background: url(/site_media/images/filetree/doc.png) left top no-repeat; } 49 | .jqueryFileTree LI.ext_exe { background: url(/site_media/images/filetree/application.png) left top no-repeat; } 50 | .jqueryFileTree LI.ext_gif { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 51 | .jqueryFileTree LI.ext_fla { background: url(/site_media/images/filetree/flash.png) left top no-repeat; } 52 | .jqueryFileTree LI.ext_h { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 53 | .jqueryFileTree LI.ext_htm { background: url(/site_media/images/filetree/html.png) left top no-repeat; } 54 | .jqueryFileTree LI.ext_html { background: url(/site_media/images/filetree/html.png) left top no-repeat; } 55 | .jqueryFileTree LI.ext_jar { background: url(/site_media/images/filetree/java.png) left top no-repeat; } 56 | .jqueryFileTree LI.ext_jpg { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 57 | .jqueryFileTree LI.ext_jpeg { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 58 | .jqueryFileTree LI.ext_js { background: url(/site_media/images/filetree/script.png) left top no-repeat; } 59 | .jqueryFileTree LI.ext_lasso { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 60 | .jqueryFileTree LI.ext_log { background: url(/site_media/images/filetree/txt.png) left top no-repeat; } 61 | .jqueryFileTree LI.ext_m4p { background: url(/site_media/images/filetree/music.png) left top no-repeat; } 62 | .jqueryFileTree LI.ext_mov { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 63 | .jqueryFileTree LI.ext_mp3 { background: url(/site_media/images/filetree/music.png) left top no-repeat; } 64 | .jqueryFileTree LI.ext_mp4 { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 65 | .jqueryFileTree LI.ext_mpg { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 66 | .jqueryFileTree LI.ext_mpeg { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 67 | .jqueryFileTree LI.ext_ogg { background: url(/site_media/images/filetree/music.png) left top no-repeat; } 68 | .jqueryFileTree LI.ext_pcx { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 69 | .jqueryFileTree LI.ext_pdf { background: url(/site_media/images/filetree/pdf.png) left top no-repeat; } 70 | .jqueryFileTree LI.ext_php { background: url(/site_media/images/filetree/php.png) left top no-repeat; } 71 | .jqueryFileTree LI.ext_png { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 72 | .jqueryFileTree LI.ext_ppt { background: url(/site_media/images/filetree/ppt.png) left top no-repeat; } 73 | .jqueryFileTree LI.ext_psd { background: url(/site_media/images/filetree/psd.png) left top no-repeat; } 74 | .jqueryFileTree LI.ext_pl { background: url(/site_media/images/filetree/script.png) left top no-repeat; } 75 | .jqueryFileTree LI.ext_py { background: url(/site_media/images/filetree/script.png) left top no-repeat; } 76 | .jqueryFileTree LI.ext_rb { background: url(/site_media/images/filetree/ruby.png) left top no-repeat; } 77 | .jqueryFileTree LI.ext_rbx { background: url(/site_media/images/filetree/ruby.png) left top no-repeat; } 78 | .jqueryFileTree LI.ext_rhtml { background: url(/site_media/images/filetree/ruby.png) left top no-repeat; } 79 | .jqueryFileTree LI.ext_rpm { background: url(/site_media/images/filetree/linux.png) left top no-repeat; } 80 | .jqueryFileTree LI.ext_ruby { background: url(/site_media/images/filetree/ruby.png) left top no-repeat; } 81 | .jqueryFileTree LI.ext_sql { background: url(/site_media/images/filetree/db.png) left top no-repeat; } 82 | .jqueryFileTree LI.ext_swf { background: url(/site_media/images/filetree/flash.png) left top no-repeat; } 83 | .jqueryFileTree LI.ext_tif { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 84 | .jqueryFileTree LI.ext_tiff { background: url(/site_media/images/filetree/picture.png) left top no-repeat; } 85 | .jqueryFileTree LI.ext_txt { background: url(/site_media/images/filetree/txt.png) left top no-repeat; } 86 | .jqueryFileTree LI.ext_vb { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 87 | .jqueryFileTree LI.ext_wav { background: url(/site_media/images/filetree/music.png) left top no-repeat; } 88 | .jqueryFileTree LI.ext_wmv { background: url(/site_media/images/filetree/film.png) left top no-repeat; } 89 | .jqueryFileTree LI.ext_xls { background: url(/site_media/images/filetree/xls.png) left top no-repeat; } 90 | .jqueryFileTree LI.ext_xml { background: url(/site_media/images/filetree/code.png) left top no-repeat; } 91 | .jqueryFileTree LI.ext_zip { background: url(/site_media/images/filetree/zip.png) left top no-repeat; } -------------------------------------------------------------------------------- /compliance/media/css/tabstyle.css: -------------------------------------------------------------------------------- 1 | @media screen, print { 2 | body { 3 | background:#fff; 4 | margin:0; 5 | padding:0; 6 | color:#000; 7 | font:x-small/1.5em Georgia,Serif; 8 | voice-family: "\"}\""; voice-family:inherit; 9 | font-size:small; 10 | } html>body {font-size:small;} 11 | .yellow { 12 | background: yellow; 13 | } 14 | .green { 15 | color: green; 16 | } 17 | .red { 18 | color: red; 19 | } 20 | .orange { 21 | color: orange; 22 | } 23 | } 24 | @media screen { 25 | #header { 26 | float:left; 27 | width:100%; 28 | height:15%; 29 | background:#DAE0D2 url("/site_media/images/bg.png") repeat-x bottom; 30 | font-size:93%; 31 | line-height:normal; 32 | } 33 | #header ul { 34 | margin:0; 35 | padding:30px 10px 0; 36 | list-style:none; 37 | } 38 | #header li { 39 | float:left; 40 | background:url("/site_media/images/left.png") no-repeat left top; 41 | margin:0; 42 | padding:0 0 0 9px; 43 | } 44 | #header a { 45 | float:left; 46 | display:block; 47 | background:url("/site_media/images/right.png") no-repeat right top; 48 | padding:5px 15px 4px 6px; 49 | text-decoration:none; 50 | font-weight:bold; 51 | color:#765; 52 | } 53 | /* Commented Backslash Hack 54 | hides rule from IE5-Mac \*/ 55 | #header a {float:none;} 56 | /* End IE5-Mac hack */ 57 | #header a:hover { 58 | color:#333; 59 | } 60 | #header #current { 61 | background-image:url("/site_media/images/left_on.png"); 62 | } 63 | #header #current a { 64 | background-image:url("/site_media/images/right_on.png"); 65 | color:#333; 66 | padding-bottom:5px; 67 | } 68 | } 69 | @media screen { 70 | #print { 71 | display:block; 72 | } 73 | #noprint { 74 | display:block; 75 | } 76 | } 77 | /* when we print the detail page, suppress the header and the print button */ 78 | @media print { 79 | #print { 80 | display:block; 81 | } 82 | #noprint { 83 | display:none; 84 | } 85 | #header { 86 | display:none; 87 | } 88 | } 89 | /* try to force a landscape layout for print */ 90 | @page 91 | { 92 | size: landscape; 93 | margin: 2cm; 94 | } 95 | -------------------------------------------------------------------------------- /compliance/media/docs/Makefile: -------------------------------------------------------------------------------- 1 | default: index.html 2 | 3 | index.html.addons: 4 | ./text-docs-to-html > index.html.addons 5 | 6 | index.html: index.html.addons 7 | cat index.html.base index.html.addons index.html.footer > index.html 8 | 9 | clean: 10 | rm -f index.html index.html.addons 11 | -------------------------------------------------------------------------------- /compliance/media/docs/index.html.base: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dependency Checker Tool 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 |
    15 | 39 | 45 |
    46 | 47 |
    48 |
    49 | Overview 50 |

    51 | The Dependency Checker is a tool to explore both dynamic and static linkage 52 | dependencies of binaries and libraries built with FOSS components. Once dependencies are 53 | identified, the GUI can provide an easy to interpret visual indication of possible license 54 | issues, based on in-house license policies.

    55 | 56 |

    The system consists of two pieces, a command-line program "readelf.py" and a GUI frontend that 57 | runs in a web browser.

    58 | 59 |

    You can view the development source from 60 | git, 61 | or check it out using standard git commands: 62 |

     63 | git clone http://git.linuxfoundation.org/dep-checker.git
     64 | 
    65 |

    66 |

    Bugs can be filed under the 67 | Compliance 68 | product.

    69 |

    There is also a 70 | mail list 71 | for discussion of the tool.

    72 |
    73 | 74 |
    75 | Setup:

    76 |
    77 | 78 |
    79 | System Requirements: 80 | 81 |

    The command-line program and the GUI require python. It also runs the OS commands: 82 | file, ldd, objdump and readelf, so these should be present on your system. 83 | The GUI requires Django, along with sqlite 84 | support for the results database. A web browser is also needed to interact with the GUI. 85 | If your distribution does not provide Django, you can follow these 86 | installation instructions.

    87 | 88 |
    89 | 90 |
    91 | Installation:

    92 |
    93 | 94 |
    95 | From Packages: 96 | 97 |

    The program is packaged as an rpm package, with dependencies on python-django. If your 98 | system does not provide django, or it's named differently, you may need to install using 99 | --nodeps: 100 | 101 |

    102 | rpm -Uvh dep-checker-0.0.5-1.noarch.rpm --nodeps
    103 | 
    104 | 105 |

    Note: If you had to use --nodeps, then you must make sure django is installed 106 | and functional on your system. Both the command line program and the gui depend on django.

    107 | 108 | The installation creates a "compliance" user/group and should create a desktop menu entry 109 | to launch the server and open the GUI in your web browser.

    110 | 111 |

    In the future we may bundle django with the package to make things simpler, as well as 112 | provide .deb packaging.

    113 | 114 |
    115 | 116 |
    117 | From Git: 118 | 119 |

    You can also checkout the project from git and run it in place: 120 | 121 |

    122 | git clone http://git.linuxfoundation.org/dep-checker.git
    123 | cd dep-checker
    124 | 
    125 | 126 | Alternately, you can get the latest tarball from the 127 | git web page by 128 | clicking on the snapshot link in the upper right-hand part of the page.

    129 | 130 |

    Once you have the tarball, unpack it (example, the numbers of your download may differ):

    131 |
    132 | tar -xf dep-checker-3af829ae0cc5aba33192c000ef0365ef6bced843.tar.gz
    133 | cd dep-checker
    134 | 
    135 | 136 | Create the application database and the documentation (you will need w3m to create 137 | README.txt). 138 | 139 |
    140 | make
    141 | 
    142 | 143 | If you don't have root permissions on the machine to install Django, you can install it in-place 144 | with the dep-checker install: 145 | 146 |
    147 | tar -xf Django-x.x.x.tar-gz
    148 | cp -ar Django-x.x.x/django dep-checker/compliance
    149 | cd dep-checker/bin
    150 | ln -s ../compliance/django .
    151 | 
    152 | 153 | Run the server and the gui should show up in a browser window: 154 |
    155 | ./bin/dep-checker.py start
    156 | 
    157 | 158 | To kill the django server, you can run: 159 |
    160 | ./bin/dep-checker.py stop
    161 | 

    162 | 163 |
    164 | 165 |
    166 | System Layout: 167 | 168 |

    169 | The application installs under the /opt/linuxfoundation namespace: 170 | 171 |

      172 |
    • bin - command line program and wrapper script to launch gui
    • 173 |
    • compliance - gui application tree and results database
    • 174 |
    • doc - License file
    • 175 |
    • share - Desktop menu files and icons
    • 176 |
    177 | 178 | Under the compliance tree, is a typical django project layout: 179 | 180 |
      181 |
    • compliance - sqlite results database 182 |
    • __init__.py, manage.py, settings.py, urls.py - generated by django at project 183 | creation, settings.py does have some configurable settings. None of the others should 184 | be altered.
    • 185 |
    • linkage - dep-checker GUI code
    • 186 |
    • media - static html elements such as images, css, javascript files. Documentation 187 | is also in this directory.
    • 188 |
    • templates/linkage - the dep-checker html tree
    • 189 |
    190 |
    191 | 192 |
    193 | Running the GUI server: 194 | 195 |

    To run the gui/server (as user compliance for installed package), there is a 196 | script that su's to the compliance user, starts the server and attempts to open a browser 197 | page to the GUI: 198 | 199 |

    200 | /opt/linuxfoundation/bin/dep-checker.py start
    201 | 
    202 | 203 | To stop the server run: 204 | 205 |
    206 | /opt/linuxfoundation/bin/dep-checker.py stop
    207 | 
    208 |

    209 | 210 |

    If for some reason this does not work, you can manually perform the steps to start the 211 | server: 212 | 213 |

    214 | su - compliance
    215 | cd /opt/linuxfoundation/compliance
    216 | python manage.py runserver
    217 | 
    218 | 219 | You can terminate the server from this console by hitting ctrl-C 220 |

    221 |
    222 | 223 |
    224 | Running the command line program: 225 | 226 |

    The command line program is called readelf.py, and it resides in 227 | /opt/linuxfoundation/bin: 228 | 229 |

    230 | Usage: readelf.py [options] <file/dir tree to examine> [recursion depth]
    231 | 
    232 | Options:
    233 |   -c                   output in csv format
    234 |   -d                   write the output into the results database
    235 |   -s DIR               directory tree to search
    236 |   --comments=COMMENTS  test comments (when writing to database)
    237 |   --project=PROJECT    project name (when writing to database)
    238 |   --no-static          don't look for static dependencies
    239 |   --version            show program's version number and exit
    240 |   -h, --help           show this help message and exit
    241 | 
    242 | 243 | The -c option is primarily used to pass data to the GUI. The format without this 244 | argument is more human-readable if you are using the command line directly. 245 | 246 |

    247 | The -s option expects a directory as an argument. If you specify this option, the 248 | program will attempt to drill down through the directory mentioned to find only files 249 | with the name specified by the next argument to analyse: 250 | 251 |

    252 | /opt/linuxfoundation/bin/readelf.py -s /foo bar
    253 | 
    254 | 255 | The program will search everything under /foo, for ELF files named bar 256 |

    257 | 258 |

    Specifying only a directory will search and report on every ELF file in that 259 | directory tree: 260 | 261 |

    262 | /opt/linuxfoundation/bin/readelf.py /foo
    263 | 
    264 | 265 | Specifying only a file will attempt to test only the specified file: 266 | 267 |
    268 | /opt/linuxfoundation/bin/readelf.py /foo/bar/baz
    269 | 
    270 | 271 | The recursion level is an optional argument, that will attempt to not only report the 272 | direct dependencies, but also report the dependencies of each library used by 273 | the target file: 274 | 275 |
    276 | /opt/linuxfoundation/bin/readelf.py /foo/bar/baz 4
    277 | 
    278 | 279 | This would attempt to recurse down 4 levels from the target file, giving output something 280 | like this: 281 | 282 |
    283 | [1]/foo/bar/baz:
    284 |   libtermcap.so.2
    285 |   [2]/lib/libtermcap.so.2.0.8:
    286 |     libc.so.6
    287 |     [3]/lib/i686/libc-2.10.1.so:
    288 |       ld-linux.so.2
    289 | [1]/foo/bar/baz:
    290 |   libdl.so.2
    291 |   [2]/lib/libdl-2.10.1.so:
    292 |     libc.so.6
    293 |     [3]/lib/i686/libc-2.10.1.so:
    294 |       ld-linux.so.2
    295 |   [2]/lib/libdl-2.10.1.so:
    296 |     ld-linux.so.2
    297 | [1]/foo/bar/baz:
    298 |   libc.so.6
    299 |   [2]/lib/i686/libc-2.10.1.so:
    300 |     ld-linux.so.2
    301 | 
    302 | 303 | You will note that even though we asked for a recursion level of 4, the test stopped at 304 | level 3, as the program detects when no further recursion is possible. 305 |

    306 | 307 |

    Static library dependencies will appear with (static) appended to the SONAME: 308 | 309 |

    310 | libncurses.so.5 (static)
    311 | 

    312 | 313 |

    The --no-static option suppresses trying to resolve staticly linked dependencies.

    314 | 315 |

    The -d, --project, and --comments are for using the command line program 316 | to feed results into the database used by the gui. Setting -d forces -c and 317 | compiles the collected results into a list that is fed to the compliance database, where it 318 | will show up with the results of tests executed from the gui. The --project and 319 | --comments are optional, as they are from the Check Dependencies tab. Multi-word 320 | strings should be enclosed in quotes. Here is an example:

    321 | 322 |
    323 | /opt/linuxfoundation/bin/readelf.py -d --project=test --comments='this is a test' /usr/bin/foo
    324 | 
    325 | 326 |

    All the other options, such as searching, recursion, and disabling static checking are also 327 | available in this mode, and the program will still output error conditions and the data to stdout.

    328 | 329 |
    330 | 331 |
    332 | Accessing the GUI: 333 |

    334 | If a browser does not open by launching the menu item, you can access the GUI 335 | (once the server is started): at 336 | http://127.0.0.1:8000/linkage. 337 |

    338 |
    339 | 340 |
    341 | Using the GUI: 342 | 343 |

    The GUI interface is pretty straightforward, with tabs to access various aspects of 344 | program: 345 | 346 |

      347 |
    • Check Dependencies - Test entry, initiate form
    • 348 |
    • Review Results - Tabular list of existing test results
    • 349 |
    • Licenses - License/alias entry tab
    • 350 |
    • License Bindings - Define license bindings for targets (test files) and libraries
    • 351 |
    • License Policies - Define sets of target/dependency license policies, to be flagged during testing
    • 352 |
    • Settings - View and change other settings, and reload the static database
    • 353 |
    • Documentation - This documentation
    • 354 |
    355 | 356 | The final page, which isn't visible in the tabs, is the test results detail page, 357 | brought up by either running a test, or clicking on the link in the results page. 358 |

    359 | 360 |
    361 | Check Dependencies: 362 | 363 |

    A test sequence would typical start at the Check Dependencies page, where you enter 364 | the test criteria. This setup parallels the operation of the command line program, where you 365 | select whether to search for a file under a directory, test a whole directory, or just 366 | a single file. There is also a drop-down to select the recursion level. You can disable 367 | checking for static dependencies via a checkbox.

    368 | 369 |

    The user field is pre-populated with the compliance user, but can be 370 | overridden. The project and comments fields are optional for your use in 371 | tracking tests.

    372 | 373 |

    Once you enter the test criteria, click on the Run Dependency Check button. After the test 374 | runs you will be presented with the detailed test results in tabular form. Depending on 375 | the number of files to be tested and the recursion level, the test can take a few minutes, 376 | so be patient.

    377 | 378 |

    Until there are licenses and bindings defined, the results detail will show TBD for 379 | both the target and dependency licenses. Now that there is data in the system, you can go back 380 | and define these relationships and update the test data.

    381 | 382 |

    There is a Print Results button on the detail page that should open the browser print 383 | dialog to print to a physical printer or to a file. Some parts of the GUI are hidden in the 384 | printed output so that only the test results show up in the printed report.

    385 | 386 |
    387 | 388 |
    389 | Review Results: 390 | 391 |

    The test results should also be accessible from the Review Results page. This is a tabular 392 | list of all the test runs, sorted by test id/date. The far-right column has the information 393 | entered from the Check Dependencies tab. Clicking on the link for the target file or directory will 394 | open the detail tab. If you want to delete test results, you can select the checkboxes and 395 | delete them from here, using the Delete Selected Tests button.

    396 |
    397 | 398 |
    399 | Licenses: 400 | 401 |

    The License tab lets you enter license/version info. You enter the license name 402 | (example: GPL) in the left-hand field and the version (example: 3.0) in the 403 | right-hand field. Like the Review Results tab, you can select and delete licenses using 404 | the checkboxes and the Delete Selected Licenses button. The license-version combination 405 | will be concatenated in the report to look like: GPL 3.0.

    406 |

    This tab also contains the entry form to map the license/version info used by the application 407 | to any possible string variations provided by imported data from other sources. You can 408 | select a license/version from the system and then provide up to 9 alternate names (aliases) 409 | that will be considered equivalent when examining test results for policy violations. Additional 410 | aliases can be added to an existing list by simply selecting the license, entering just the new 411 | alias and clicking "Add" again.

    412 | 413 |
    414 | 415 |
    416 | License Bindings: 417 | 418 |

    The License Bindings tab lets you define the license binding for the target files, 419 | that is, the files that are being tested for dependencies. The same type of bindings can be done 420 | for the dependency libraries.

    421 | 422 |

    The drop down under Target will show all files having test data. The drop-down 423 | under License will show all the licenses defined in the License tab. If there 424 | is no test-data or no licenses, the drop-downs will be empty. If there is test data in the 425 | system, you can update the license information for current data using the 426 | Update Target Test Data button.

    427 | 428 |

    The drop down under Library will show all libraries in the test data. 429 | The drop-down under License will show all the licenses defined in the License 430 | tab. The License selector does not differentiate between static and dynamic versions, 431 | both will be treated the same. If there is no test-data or no licenses, the drop-downs will 432 | be empty. If there is test data in the system, you can update the license information for 433 | current data using the Update Library Test Data button.

    434 | 435 |
    436 | 437 |
    438 | License Policies: 439 | 440 |

    The License Policies tab lets you define pairing of target/library licenses that could 441 | have potential issues. You select the Target License and Library License from the 442 | drop-downs and then select the relationship, either Static, Dynamic, or both. You can also 443 | set the state or either Approve or Disapprove. When a test is run, violations of 444 | these policy settings will show up the the report detail printed in red with a red flag after 445 | the License name. License pairings that are approved will have normal black text, and 446 | unknown/undefined pairings will be highlighted in orange with an orange flag. Like the other 447 | tabs, you can select and delete policies using the checkboxes and the 448 | Delete Selected Policies button.

    449 | 450 |

    In the screenshot below, you can see an example of a flagged policy violation. The application 451 | myapp has been compiled against libmylib.so. The licenses: L1 2.0 and 452 | L2 1.3 have been defined in the policy screen as being an issue. When the test data is 453 | displayed, this relationship is flagged as being problematic:

    454 | 455 |

    policy_flag.png

    456 | 457 |

    If the target (file) or library is using a license naming convention that is not defined 458 | in the application licenses tab, but has a naming convention defined as equivalent in the 459 | aliases table, the license violation will look like:

    460 | 461 |
    462 | alias name (real name) [graphical flag]
    463 | 
    464 | 465 |
    466 | 467 |
    468 | Settings: 469 | 470 |

    The Settings tab lets you change the static data used to 471 | detect static libraries in use by the program being tested.

    472 | 473 |

    The symbol data used for static detection is based on the libraries 474 | currently installed on the test system. You can reload the data by 475 | activating the Reload Static Data button at the top of the page.

    476 | 477 |

    By default, system libraries from the normal system paths are loaded 478 | into the database for static symbol detection. The list of paths to search 479 | is provided in a large edit box, one per line; you can add or remove paths 480 | from this list, and activate the Save Changes button to save those 481 | search paths.

    482 | 483 |
    484 | 485 |
    486 | 487 |
    488 | Admin Interface: 489 | 490 |

    In the current configuration, the django admin interface is enabled. While you can use 491 | this interface to directly access the database records, one should take care not to alter 492 | existing records, except in the case of wishing to add license information to records.

    493 | 494 |

    admin interface: http://127.0.0.1:8000/admin 495 | (username compliance, password compliance) 496 |

    497 |
    498 | 499 |
    500 | Database Schema: 501 | 502 |

    The database for the application is in the file compliance in the compliance 503 | directory. It is an sqlite3 database file. Tables used by the application are as follows 504 | (arranged more or less as they are integrated into the application tabs):

    505 | 506 |
      507 |
    • linkage_test - table containing the information entered from the Test tab. Each 508 | test has an id used to track the test and the relationships to the file and lib 509 | tables, although the id is not shown in the gui, only the date/time.
    • 510 |
        511 |
      • id - test id, used for linking to the file and lib tables (primary key)
      • 512 |
      • do_search - boolean value used to determine whether to search a directory tree 513 | for a particular filename
      • 514 |
      • disable_static - boolean value used to determine whether to test for static 515 | dependencies
      • 516 |
      • test_done - boolean value used to suppressed test from the results page before it is 517 | completed
      • 518 |
      • recursion - recursion level for dependency checking, default is 1
      • 519 |
      • target - target file or directory
      • 520 |
      • target_dir - target directory when searching for a file by name
      • 521 |
      • test_date - test date/time
      • 522 |
      • user - username entered on the test form
      • 523 |
      • project - project name entered on the test form
      • 524 |
      • comments - free-form comments field from the test form
      • 525 |
      526 |
    • linkage_file - target files examined during a test run. Linked to the 527 | test table via test_id.
    • 528 |
        529 |
      • id - record id, not exposed in the gui (primary key)
      • 530 |
      • test_id - reference to id from the test table
      • 531 |
      • file - file name/path. In the recursive case, can be a library path.
      • 532 |
      • license - file license
      • 533 |
      • level - recursion level. Top-level file tested would be level 1. True paths to the 534 | dependent libraries on the system under test would show up here as higher 535 | levels when doing recursive testing (these files are not exposed in the GUI).
      • 536 |
      • parent_id - When doing recursion, each child file has a parent. This field 537 | captures that relationship.
      • 538 |
      • checked_static - flag to indicate whether static checking was done on this file
      • 539 |
      540 |
    • linkage_lib - library dependencies of the files from the files table.
    • 541 |
        542 |
      • id - record id, not exposed in the gui (primary key)
      • 543 |
      • test_id - reference to id from the test table
      • 544 |
      • file_id - reference to id from the file table
      • 545 |
      • library - library SONAME
      • 546 |
      • static - flag to indicate whether the library is static or dynamic
      • 547 |
      • license - library license
      • 548 |
      • level - recursion level
      • 549 |
      • parent_id - When doing recursion, each child library has a parent. This field 550 | captures that relationship.
      • 551 |
      552 |
    • linkage_license - This table is not linked to any of the test data tables, but 553 | is used to populate the target/library licenses and license policy drop-downs, which in turn 554 | fills in the license data for a test and flags policy issues.
    • 555 |
        556 |
      • id - record id, not exposed in the gui (primary key)
      • 557 |
      • longname - long version of license string (e.g. GNU Public License)
      • 558 |
      • license - abbreviated license string (e.g. GPL)
      • 559 |
      • version - license version number (e.g. 3.0)
      • 560 |
      561 |
    • linkage_aliases - This table is not linked to any of the test data tables, but 562 | is used to map possible license string variations from outside sources to the names defined 563 | in the license policies. There is a many-to-one relationship between license and alias, with 564 | each alias entry needing to be unique.
    • 565 |
        566 |
      • id - record id, not exposed in the gui (primary key)
      • 567 |
      • license - concatenation of license/version in the same form generated from the license table
      • 568 |
      • alias - any acceptable variation that is considered equivalent (e.g. GPLv3, GPL3)
      • 569 |
      570 |
    • linkage_filelicense - target file/license bindings. These are not linked to 571 | any other table, but the information, if present, is used to fill in the license field in the 572 | file table after a test run, or you can manually update the data from the Target 573 | Licenses tab.
    • 574 |
        575 |
      • id - record id, not exposed in the gui (primary key)
      • 576 |
      • file - file name/path, selected from the file table
      • 577 |
      • license - selected concatenation of license/version from the license table
      • 578 |
      579 |
    • linkage_liblicense - library/license bindings. These are not linked to 580 | any other table, but the information, if present, is used to fill in the license field in the 581 | lib table after a test run, or you can manually update the data from the Library 582 | Licenses tab.
    • 583 |
        584 |
      • id - record id, not exposed in the gui (primary key)
      • 585 |
      • library - library SONAME, selected from the lib table
      • 586 |
      • license - selected concatenation of license/version from the license table
      • 587 |
      588 |
    • linkage_policy
    • 589 |
        590 |
      • id - record id, not exposed in the gui (primary key)
      • 591 |
      • tlicense - target license selected from the concatenation of license/version from the license table
      • 592 |
      • dlicense - library license selected from the concatenation of license/version from the license table
      • 593 |
      • relationship - relationship string, either 'Static', 'Dynamic', or 'Both'
      • 594 |
      • rank - problem ranking - currently not used
      • 595 |
      • status - character flag for Approved (A) or Disapproved (D)
      • 596 |
      • edit_date - date/time the policy was entered
      • 597 |
      598 |
    • linkage_staticsymbol
    • 599 |
        600 |
      • id - record id, not exposed in the gui (primary key)
      • 601 |
      • symbol - symbol name. symbols extracted from the target under test will match this entry to get possible library sources
      • 602 |
      • libraryname - Library SONAME that provides this symbol
      • 603 |
      604 |
    605 | 606 |
    607 | 608 |
    609 | Importing License Data: 610 | 611 |

    Because linkage_license, linkage_aliases, linkage_filelicense, linkage_liblicense, and 612 | linkage_policy are more or less independent of the test data, one could easily load these 613 | tables from other data sources, using sqlite3, as long as the existing table schemas are honored. 614 | To illustrate, let's walk through an example of importing a file library/license mappings from 615 | another source.

    616 | 617 |

    Say have a csv file of library,license data like this: 618 | 619 |

    620 | libfoo.so.6,LPGLv3
    621 | libbar.so.2,BSD1
    622 | libbaz.so.4,APACHE 2
    623 | 
    624 | 625 | We can easily process this into SQL statements we can load into dep-checker using whatever 626 | script language you're comfortable with. With the shell and awk, perhaps something like this: 627 | 628 |
    629 | awk -F,  '{print "INSERT INTO linkage_liblicense (library, license) VALUES (\"" $1 "\",\"" $2 "\");"}' < liblicenses.csv > liblicenses.sql
    630 | cd dep-checker compliance
    631 | sqlite3 compliance < liblicenses.sql
    632 | 
    633 | 634 | And if we look at the database now: 635 | 636 |
    637 | sqlite3 compliance 
    638 | SQLite version 3.6.23.1
    639 | Enter ".help" for instructions
    640 | Enter SQL statements terminated with a ";"
    641 | sqlite> select * from linkage_liblicense;
    642 | 1|libfoo.so.6|LPGLv3
    643 | 2|libbar.so.2|BSD1
    644 | 3|libbaz.so.4|APACHE 2
    645 | 
    646 | 647 | So our data is loaded, but we have a slight problem in that the license naming conventions from 648 | our data file don't match the format used in dep-checker to feed into our license polices. In 649 | dep-checker, "LGPLv3" would be "LGPL 3.0", "BSD1" would be 650 | "BSD 1.0" and "APACHE 2" might be "Apache 2.0". We can correct 651 | this either by define alias mappings in the Licenses tab or with some additional SQL 652 | (assuming we know the "correct" naming defined in dep-checker): 653 | 654 |
    655 | INSERT INTO linkage_aliases (license, alias) VALUES ('LGPL 3.0', 'LGPLv3');
    656 | INSERT INTO linkage_aliases (license, alias) VALUES ('BSD 1.0', 'BSD1');
    657 | INSERT INTO linkage_aliases (license, alias) VALUES ('Apache 2.0', 'APACHE 2');
    658 | 
    659 | 660 | If you have a large number of alias mappings to perform, SQL may be the way to go, otherwise they 661 | can be assigned under Aliases in the Licenses tab where you'l be assured of the 662 | correct mappings, as only "known" licenses will be available in the drop-down.

    663 | 664 |

    A similar process could be used to load license associations for target files.

    665 | 666 |

    Once the license data is loaded, the functions update_file_bindings() and 667 | update_lib_bindings() in views.py would apply this information to existing test data, 668 | which can also be done from the gui Target Licenses and Library Licenses tabs by 669 | clicking the Update Test Data button.

    670 | 671 |
    672 | 673 |
    674 | How it Works: 675 | 676 |

    As mentioned earlier, the command line program, readelf.py does all the actual file search and 677 | analysis. For discovering dependencies, it uses readelf, ldd, and file, using the 678 | following methodology: 679 | 680 |

      681 |
    • Search for file?
    • 682 |
        683 |
      • Yes - start search at top of directory tree specified, for the file name in question
      • 684 |
      • No - start processing specified file or walk the whole specified directory tree
      • 685 |
      686 |
    • File found - elf file? (using "file")
    • 687 |
        688 |
      • No - return "not an elf file", continue processing more files or exit
      • 689 |
      • Yes - check whether static or dynamically linked
      • 690 |
          691 |
        • Dynamic - run "readelf -d" on the file and capture all the SONAMES tagged as NEEDED, followup with static analysis
        • 692 |
            693 |
          • Recursive analysis?
          • 694 |
              695 |
            • Yes - run "ldd" on the test file to get the paths to the libraries associated with the SONAME, repeat the analysis on each of these libraries, and in turn their dependencies, stopping when we either reach the desired recursion level or glibc/ld-linux and no further recursion is possible
            • 696 |
            • No - output the results and exit or process the next file
            • 697 |
            698 |
          699 |
        • Static
        • 700 |
            701 |
          • run "readelf -s" on the file and capture all the functions from the symbol list
          • 702 |
          • run "readelf -wi" on the file and capture all the functions from the debug info
          • 703 |
          • isolate the functions that have no debug info, these are considered as coming from another library
          • 704 |
          • look up possible sources of the function from the database and report
          • 705 |
          706 |
        707 |
      708 |
    709 |
    710 | 711 |
    712 | Limitations: 713 | 714 |

    There are certain limitations in the analysis of binaries/libraries for static/dynamic 715 | dependencies.

    716 | 717 |

    In the static case, the symbol table is created either on the build server 718 | (packaged version), or the user's machine (run in-place from git). The content of the table 719 | will be largely driven by the libraries present on the system, and may not completely reflect 720 | the system where the target files have been built.

    721 | 722 |

    Also, the same symbol can come from one than one library, and the tool can only provide the 723 | possible sources of the symbol in question. Some deeper investigation of the actual build system 724 | may be required to identify the actual static linkage.

    725 | 726 |

    For the dynamic case, the level one dependencies are pretty clear-cut, but for the recursive 727 | case one can get a slightly different set of dependencies when drilling down into the system 728 | libraries, depending on how the system libraries themselves are built.

    729 | 730 |
    731 | 732 | 733 | -------------------------------------------------------------------------------- /compliance/media/docs/index.html.footer: -------------------------------------------------------------------------------- 1 | 2 |
    3 | 4 |
     
    5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /compliance/media/docs/policy_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/docs/policy_flag.png -------------------------------------------------------------------------------- /compliance/media/docs/text-docs-to-html: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | import cgi 4 | 5 | # process some plain text files and append to index.html 6 | # gets around using inline frames and the quasi-html plain text files 7 | 8 | doc = '' 9 | doc_root = '../../../' 10 | files = ('AUTHORS', 'Changelog') 11 | 12 | def html_ize(root, files): 13 | d = '' 14 | for file in files: 15 | d += '
    \n' 16 | d += '' + file.capitalize() + ':\n' 17 | d += '
    \n'
    18 |         f = open(root + file, 'r')
    19 |         for line in f:
    20 |             d += cgi.escape(line, quote=None)
    21 |         f.close()
    22 |         d += '
    \n' 23 | d += '
    \n\n' 24 | return d 25 | 26 | doc += html_ize(doc_root, files) 27 | 28 | doc_root = '../../../doc/' 29 | files = ('Contributing', 'LICENSE') 30 | 31 | doc += html_ize(doc_root, files) 32 | 33 | print doc 34 | -------------------------------------------------------------------------------- /compliance/media/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/bg.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/application.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/bg.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/code.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/css.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/db.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/directory.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/doc.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/file.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/film.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/film.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/flash.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/folder_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/folder_open.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/html.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/java.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/linux.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/music.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/pdf.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/php.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/picture.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/ppt.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/psd.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/ruby.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/script.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/spinner.gif -------------------------------------------------------------------------------- /compliance/media/images/filetree/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/txt.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/xls.png -------------------------------------------------------------------------------- /compliance/media/images/filetree/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/filetree/zip.png -------------------------------------------------------------------------------- /compliance/media/images/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/left.png -------------------------------------------------------------------------------- /compliance/media/images/left_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/left_on.png -------------------------------------------------------------------------------- /compliance/media/images/lfc_dep_checker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfc_dep_checker.jpg -------------------------------------------------------------------------------- /compliance/media/images/lfc_dep_checker_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfc_dep_checker_small.png -------------------------------------------------------------------------------- /compliance/media/images/lfc_dep_checker_small.png.org: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfc_dep_checker_small.png.org -------------------------------------------------------------------------------- /compliance/media/images/lfdc_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfdc_logo.jpg -------------------------------------------------------------------------------- /compliance/media/images/lfdc_logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfdc_logo_large.png -------------------------------------------------------------------------------- /compliance/media/images/lfdc_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lfdc_logo_small.png -------------------------------------------------------------------------------- /compliance/media/images/lflogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/lflogo.png -------------------------------------------------------------------------------- /compliance/media/images/orange_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/orange_flag.png -------------------------------------------------------------------------------- /compliance/media/images/red_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/red_flag.png -------------------------------------------------------------------------------- /compliance/media/images/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/right.png -------------------------------------------------------------------------------- /compliance/media/images/right_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/compliance/media/images/right_on.png -------------------------------------------------------------------------------- /compliance/media/js/checklist.js: -------------------------------------------------------------------------------- 1 | // some generic functions for selecting/deselecting/deleting a number of table entries 2 | 3 | function toggleall(f,icheck,allcheck) { 4 | chk = document.forms[f.name].elements[icheck]; 5 | if (document.forms[f.name].elements[allcheck].checked == true) { 6 | // if only one test, no array 7 | if (typeof chk.length == 'undefined') { 8 | chk.checked = true; 9 | } else { 10 | for (i = 0; i < chk.length; i++) { 11 | chk[i].checked = true; 12 | } 13 | } 14 | } else { 15 | if (typeof chk.length == 'undefined') { 16 | chk.checked = false; 17 | } else { 18 | for (i = 0; i < chk.length; i++) { 19 | chk[i].checked = false; 20 | } 21 | } 22 | } 23 | } 24 | 25 | function buildlist(f,icheck,ilist) { 26 | chk = document.forms[f.name].elements[icheck]; 27 | llist = ''; 28 | if (typeof chk.length == 'undefined') { 29 | llist = chk.value; 30 | } else { 31 | for (i = 0; i < chk.length; i++) { 32 | if (chk[i].checked == true) { 33 | llist = llist + chk[i].value + ","; 34 | } 35 | } 36 | } 37 | document.forms[f.name].elements[ilist].value = llist; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /compliance/media/js/jqueryFileTree.js: -------------------------------------------------------------------------------- 1 | // jQuery File Tree Plugin 2 | // 3 | // Version 1.01 4 | // 5 | // Cory S.N. LaViska 6 | // A Beautiful Site (http://abeautifulsite.net/) 7 | // 24 March 2008 8 | // 9 | // Visit http://abeautifulsite.net/notebook.php?article=58 for more information 10 | // 11 | // Usage: $('.fileTreeDemo').fileTree( options, callback ) 12 | // 13 | // Options: root - root folder to display; default = / 14 | // script - location of the serverside AJAX file to use; default = jqueryFileTree.php 15 | // folderEvent - event to trigger expand/collapse; default = click 16 | // expandSpeed - default = 500 (ms); use -1 for no animation 17 | // collapseSpeed - default = 500 (ms); use -1 for no animation 18 | // expandEasing - easing function to use on expand (optional) 19 | // collapseEasing - easing function to use on collapse (optional) 20 | // multiFolder - whether or not to limit the browser to one subfolder at a time 21 | // loadMessage - Message to display while initial tree loads (can be HTML) 22 | // 23 | // History: 24 | // 25 | // 1.01 - updated to work with foreign characters in directory/file names (12 April 2008) 26 | // 1.00 - released (24 March 2008) 27 | // 28 | // TERMS OF USE 29 | // 30 | // This plugin is dual-licensed under the GNU General Public License and the MIT License and 31 | // is copyright 2008 A Beautiful Site, LLC. 32 | // 33 | if(jQuery) (function($){ 34 | 35 | $.extend($.fn, { 36 | fileTree: function(o, h) { 37 | // Defaults 38 | if( !o ) var o = {}; 39 | if( o.root == undefined ) o.root = '/'; 40 | if( o.script == undefined ) o.script = 'jqueryFileTree.php'; 41 | if( o.folderEvent == undefined ) o.folderEvent = 'click'; 42 | if( o.expandSpeed == undefined ) o.expandSpeed= 500; 43 | if( o.collapseSpeed == undefined ) o.collapseSpeed= 500; 44 | if( o.expandEasing == undefined ) o.expandEasing = null; 45 | if( o.collapseEasing == undefined ) o.collapseEasing = null; 46 | if( o.multiFolder == undefined ) o.multiFolder = true; 47 | if( o.loadMessage == undefined ) o.loadMessage = 'Loading...'; 48 | 49 | $(this).each( function() { 50 | 51 | function showTree(c, t) { 52 | $(c).addClass('wait'); 53 | $(".jqueryFileTree.start").remove(); 54 | $.post(o.script, { dir: t }, function(data) { 55 | $(c).find('.start').html(''); 56 | $(c).removeClass('wait').append(data); 57 | if( o.root == t ) $(c).find('UL:hidden').show(); else $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing }); 58 | bindTree(c); 59 | }); 60 | } 61 | 62 | function bindTree(t) { 63 | $(t).find('LI A').bind(o.folderEvent, function() { 64 | if( $(this).parent().hasClass('directory') ) { 65 | if( $(this).parent().hasClass('collapsed') ) { 66 | // Expand 67 | if( !o.multiFolder ) { 68 | $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); 69 | $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed'); 70 | } 71 | $(this).parent().find('UL').remove(); // cleanup 72 | showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) ); 73 | $(this).parent().removeClass('collapsed').addClass('expanded'); 74 | } else { 75 | // Collapse 76 | $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); 77 | $(this).parent().removeClass('expanded').addClass('collapsed'); 78 | } 79 | // we need to capture directory names sometimes too (sb) 80 | h($(this).attr('rel')); 81 | } else { 82 | h($(this).attr('rel')); 83 | } 84 | return false; 85 | }); 86 | 87 | // Prevent A from triggering the # on non-click events 88 | if( o.folderEvent.toLowerCase != 'click' ) $(t).find('LI A').bind('click', function() { return false; }); 89 | } 90 | // Loading message 91 | $(this).html('
    • ' + o.loadMessage + '
    '); 92 | // Get the initial file list 93 | showTree( $(this), escape(o.root) ); 94 | }); 95 | } 96 | }); 97 | 98 | })(jQuery); 99 | -------------------------------------------------------------------------------- /compliance/media/js/visibility.js: -------------------------------------------------------------------------------- 1 | function toggle_visibility(id) { 2 | var e = document.getElementById(id); 3 | if (e.style.display == 'block') 4 | e.style.display = 'none'; 5 | else 6 | e.style.display = 'block'; 7 | } 8 | -------------------------------------------------------------------------------- /compliance/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for compliance project. 2 | 3 | import os 4 | import os.path 5 | import time 6 | 7 | # Function for finding the project root. 8 | 9 | def get_project_root(): 10 | current_path = os.path.abspath(__file__) 11 | while current_path != "/": 12 | (current_path, fn) = os.path.split(current_path) 13 | if os.path.basename(current_path) != "local" and \ 14 | os.path.exists(os.path.join(current_path, "bin/readelf.py")): 15 | return current_path 16 | 17 | # If that doesn't work, try an old common path. 18 | if os.path.exists("/opt/linuxfoundation/bin/readelf.py"): 19 | return "/opt/linuxfoundation" 20 | 21 | # Shouldn't get here unless we can't find the path. 22 | raise RuntimeError, "could not find the project path" 23 | 24 | # Return the proper directory to use for userdir mode. 25 | 26 | def get_userdir(): 27 | return os.path.join(os.environ["HOME"], ".dep-checker") 28 | 29 | # Should we use userdir mode? 30 | 31 | def use_userdir(): 32 | if os.getuid() == 0 or os.environ["LOGNAME"] == "compliance": 33 | return False 34 | project_root = get_project_root() 35 | if os.access(os.path.join(project_root, "compliance"), os.W_OK): 36 | return False 37 | 38 | return True 39 | 40 | # Find the doc root. 41 | 42 | def get_doc_root(candidate_paths, default_path): 43 | docdir = None 44 | for d in candidate_paths: 45 | if os.path.exists(os.path.join(d, "docs/index.html")): 46 | docdir = d 47 | break 48 | 49 | return (docdir and docdir or default_path) 50 | 51 | DEBUG = True 52 | TEMPLATE_DEBUG = DEBUG 53 | 54 | ALLOWED_HOSTS = '*' 55 | 56 | ADMINS = ( 57 | # ('Your Name', 'your_email@domain.com'), 58 | ) 59 | 60 | MANAGERS = ADMINS 61 | 62 | DATABASES = { 63 | 'default': { 64 | 'ENGINE': 'django.db.backends.sqlite3', 65 | } 66 | } 67 | 68 | # Local time zone for this installation. Choices can be found here: 69 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 70 | # although not all choices may be available on all operating systems. 71 | # If running in a Windows environment this must be set to the same as your 72 | # system time zone. 73 | 74 | if 'TZ' not in os.environ: # env is clear => timezone is the system one 75 | TIME_ZONE = 'Etc/GMT%+d' % (time.altzone / 3600) 76 | else: # env is updated => timezone is obsolete 77 | TIME_ZONE = os.environ['TZ'] 78 | 79 | # Language code for this installation. All choices can be found here: 80 | # http://www.i18nguy.com/unicode/language-identifiers.html 81 | LANGUAGE_CODE = 'en-us' 82 | 83 | SITE_ID = 1 84 | 85 | # If you set this to False, Django will make some optimizations so as not 86 | # to load the internationalization machinery. 87 | USE_I18N = False 88 | 89 | # Project root. 90 | PROJECT_ROOT = get_project_root() 91 | 92 | # Command-line client. 93 | CLI_COMMAND = os.path.join(PROJECT_ROOT, 'bin/readelf.py') 94 | 95 | # Writable file setup; use different settings for userdir or normal mode. 96 | if use_userdir(): 97 | USERDIR_ROOT = get_userdir() 98 | DATABASES['default']['NAME'] = os.path.join(USERDIR_ROOT, "compliance") 99 | STATE_ROOT = USERDIR_ROOT 100 | else: 101 | USERDIR_ROOT = '' 102 | DATABASES['default']['NAME'] = os.path.join(get_project_root(), 103 | 'compliance', 'compliance') 104 | STATE_ROOT = os.path.join(PROJECT_ROOT, 'compliance') 105 | 106 | # Absolute path to the directory that holds media. 107 | # Example: "/home/media/media.lawrence.com/" 108 | MEDIA_ROOT = '' 109 | 110 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 111 | # trailing slash if there is a path component (optional in other cases). 112 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 113 | MEDIA_URL = '' 114 | 115 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 116 | # trailing slash. 117 | # Examples: "http://foo.com/media/", "/media/". 118 | ADMIN_MEDIA_PREFIX = '/media/' 119 | STATIC_URL = '/site_media/' 120 | 121 | # Make this unique, and don't share it with anybody. 122 | SECRET_KEY = 'wzb!69=4(kj=w)vl&lyp-j1ff9#fi8^)p^i4xr9$kokcu5j9pk' 123 | 124 | # List of callables that know how to import templates from various sources. 125 | TEMPLATE_LOADERS = ( 126 | 'django.template.loaders.filesystem.Loader', 127 | 'django.template.loaders.app_directories.Loader', 128 | # 'django.template.loaders.eggs.Loader', 129 | ) 130 | 131 | MIDDLEWARE_CLASSES = ( 132 | 'django.middleware.common.CommonMiddleware', 133 | 'django.contrib.sessions.middleware.SessionMiddleware', 134 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 135 | ) 136 | 137 | ROOT_URLCONF = 'compliance.urls' 138 | 139 | TEMPLATE_DIRS = ( 140 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 141 | # Always use forward slashes, even on Windows. 142 | # Don't forget to use absolute paths, not relative paths. 143 | os.path.join(os.path.dirname(__file__), "templates"), 144 | os.path.join(PROJECT_ROOT, "compliance/templates"), 145 | ) 146 | 147 | STATICFILES_DIRS = ( 148 | os.path.join(os.path.dirname(__file__), "media"), 149 | os.path.join(PROJECT_ROOT, "compliance/media"), 150 | ) 151 | 152 | STATIC_DOC_ROOT = get_doc_root(list(STATICFILES_DIRS), 153 | os.path.join(PROJECT_ROOT, "compliance/media")) 154 | 155 | INSTALLED_APPS = ( 156 | 'django.contrib.auth', 157 | 'django.contrib.contenttypes', 158 | 'django.contrib.sessions', 159 | 'django.contrib.sites', 160 | 'django.contrib.admin', 161 | 'django.contrib.staticfiles', 162 | 'compliance.linkage', 163 | ) 164 | -------------------------------------------------------------------------------- /compliance/task.py: -------------------------------------------------------------------------------- 1 | # task.py - task management 2 | # Copyright 2010 Linux Foundation. 3 | # Jeff Licquia 4 | 5 | # This task manager was originally created for the Dependency Checker 6 | # and Code Janitor projects of the Linux Foundation. It's fairly 7 | # simple to use; just create a function containing the task you need 8 | # done asynchronously, create a TaskManager object, and call its 9 | # start() function, passing in the task function you created. Since 10 | # only one task at a time can be run, it's a good idea to check if 11 | # one's already running with is_running(). Any problems (including 12 | # trying to start a task if one's already running) cause the manager 13 | # to throw a TaskError, so you can catch those if you need. 14 | 15 | # Once a task is running, you can ask the manager to give you some 16 | # simple HTML to report the task's status with read_status(). It will 17 | # return a None object if no task is running, or a single HTML string 18 | # suitable for embedding into a
    . This is designed to be 19 | # embedded into a simple status URL that can be called with 20 | # XMLHttpRequest and dynamically updated with JavaScript in some other 21 | # page. 22 | 23 | # As for reporting status, your task function can write 24 | # specially-formatted lines to stdout or stderr. These take the 25 | # form: 26 | # TYPE: Data 27 | # It currently supports four types: JOBDESC, MESSAGE, COUNT, and 28 | # ITEM. JOBDESC is a short description of the job. COUNT and ITEM 29 | # work together; the job is assumed to consist of COUNT items, each of 30 | # which is reported as it is done with an ITEM line. MESSAGE is for 31 | # reporting the job's status when it's a little more complicated than 32 | # "x of y items". A MESSAGE overrides the reporting of COUNT/ITEM 33 | # until another ITEM is received, at which point the MESSAGE is 34 | # suppressed. 35 | 36 | # Your task's reporting of these statuses makes its way to the 37 | # manager's read_status() function, and is used to generate the HTML 38 | # that function returns. 39 | 40 | import os 41 | try: 42 | import cStringIO as StringIO 43 | except: 44 | import StringIO 45 | from django.conf import settings 46 | 47 | # Exceptions 48 | 49 | class TaskError(StandardError): 50 | pass 51 | 52 | class TaskManager: 53 | def __init__(self): 54 | self._task_log_fn = os.path.join(settings.STATE_ROOT, "task.log") 55 | self._task_log_file = None 56 | 57 | def _get_status_file(self): 58 | if not self.is_running(): 59 | raise TaskError, "no task is running" 60 | 61 | if not self._task_log_file: 62 | self._task_log_file = open(self._task_log_fn) 63 | 64 | return self._task_log_file 65 | 66 | def is_running(self): 67 | return os.path.exists(self._task_log_fn) 68 | 69 | def start(self, task_func): 70 | if self.is_running(): 71 | raise TaskError, "a task is already running" 72 | 73 | try: 74 | task_fd = os.open(self._task_log_fn, 75 | os.O_WRONLY | os.O_APPEND | os.O_CREAT) 76 | except OSError: 77 | raise TaskError, "could not open task log file" 78 | 79 | pid = os.fork() 80 | if pid == 0: 81 | pid = os.fork() 82 | if pid == 0: 83 | os.setsid() 84 | 85 | os.dup2(task_fd, 1) 86 | os.dup2(task_fd, 2) 87 | os.close(0) 88 | 89 | try: 90 | task_func() 91 | finally: 92 | os.close(task_fd) 93 | os.close(1) 94 | os.close(2) 95 | os.unlink(self._task_log_fn) 96 | os._exit(0) 97 | else: 98 | os._exit(0) 99 | else: 100 | os.waitpid(pid, 0) 101 | 102 | def read_status(self): 103 | if not self.is_running(): 104 | return None 105 | else: 106 | jobdesc = None 107 | total = 0 108 | running_count = 0 109 | current = "" 110 | message = None 111 | status_str = "" 112 | msgadd = "" 113 | data = self._get_status_file().read() 114 | data_as_file = StringIO.StringIO(data) 115 | for line in data_as_file: 116 | if line.find(":") != -1: 117 | (tag, detail) = line.split(":", 1) 118 | if tag == "JOBDESC": 119 | jobdesc = detail.strip() 120 | elif tag == "MESSAGE": 121 | message = detail.strip() 122 | elif tag == "MSGADD": 123 | msgadd += detail.strip() + "
    " 124 | elif tag == "COUNT": 125 | total = int(detail.strip()) 126 | elif tag == "ITEM": 127 | running_count = running_count + 1 128 | current = detail.strip() 129 | message = None 130 | if jobdesc: 131 | status_str = status_str + jobdesc 132 | if message: 133 | status_str = "%s
    %s
    " % (status_str, message) 134 | if msgadd: 135 | status_str = "%s
    %s" % (status_str, msgadd) 136 | else: 137 | status_str = status_str + "
    " 138 | if current: 139 | status_str = status_str + current 140 | if total: 141 | percent = int(float(running_count) / float(total) * 100) 142 | status_str = status_str + \ 143 | "
    " \ 144 | % percent 145 | else: 146 | status_str = "
    Processed %d items.
    " \ 147 | % (running_count - 1) 148 | return status_str 149 | -------------------------------------------------------------------------------- /compliance/templates/admin/base_site.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{{ title }} | {% trans 'Compliance Admin' %}{% endblock %} 5 | 6 | {% block branding %} 7 |

    {% trans 'Compliance' %}

    8 | {% endblock %} 9 | 10 | {% block nav-global %}{% endblock %} 11 | -------------------------------------------------------------------------------- /compliance/templates/linkage/banner.html: -------------------------------------------------------------------------------- 1 | 2 | Dependency Checker 3 | 4 | -------------------------------------------------------------------------------- /compliance/templates/linkage/base.html: -------------------------------------------------------------------------------- 1 | {% include "linkage/header.html" %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block header %} {% endblock %} 10 | 11 | 12 | 13 | 25 | 26 |
     
    27 |
    28 | {% block content %} {% endblock %} 29 |
    30 |
    31 | 32 | 33 | 34 | {% block scripts %} 35 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /compliance/templates/linkage/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | {% endblock %} 5 | 6 | {% block content %} 7 | 8 | {% if error_message %} 9 |

    No test result, or error message from cli was:

    10 |
    11 |   {{ error_message }}
    12 |   
    13 | {% else %} 14 |
    15 |
    16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 |
    Project: {{ test.project }}Target: {{ test.target }}User: {{ test.user }}Date: {{ test.test_date|date:"M d, Y H:i:s" }}Static Checks: {{ test.disable_static|yesno:"no,yes" }} 24 | 25 |
    28 |
    29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% for row in master %} 39 | 40 | 41 | {% autoescape off %} 42 | 43 | 44 | 45 | 46 | {% endautoescape %} 47 | 48 | {% endfor %} 49 |
    TargetTarget LicenseDependencyStatic
    Linkage
    Dependency License
    {{ row.file }}{{ row.license }}{{ row.libs }}{{ row.statics }}{{ row.licenses }}
    50 | 51 | {% if not test.disable_static %} 52 |
    53 |

    "Not available" in the Static Linkage column means that the necessary data 54 | for detecting statically linked binaries was not present. Static linkage 55 | detection requires that the binary being tested was built with debugging 56 | information.

    57 |
    58 | {% endif %} 59 | 60 |
    61 | {% endif %} 62 | 63 | {% endblock %} 64 | -------------------------------------------------------------------------------- /compliance/templates/linkage/documentation.html: -------------------------------------------------------------------------------- 1 | {% include "linkage/header.html" %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 21 |
    22 |
    23 | {{ name }} version {{ version }}. 24 |
    25 |
    26 | 27 | {% autoescape off %} 28 | {{ gui_docs }} 29 | {% endautoescape %} 30 | 31 | 32 | -------------------------------------------------------------------------------- /compliance/templates/linkage/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Dependency Checker Tool 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /compliance/templates/linkage/index.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | {% endblock %} 5 | 6 | {% block content %} 7 | 8 |

    This is a tool to analyze linked and static dependencies of compiled ELF binaries and libraries.

    9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /compliance/templates/linkage/lbindings.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | 5 | 6 | 7 | {% endblock %} 8 | 9 | {% block content %} 10 |
    11 |
    12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {% for target in latest_targetlicense_list %} 43 | 44 | 45 | 46 | 47 | 48 | {% endfor %} 49 |
    Target: 
    License: 
    {{ targetlicenseform.file }}
    {{ targetlicenseform.license }}
    27 | 29 | 32 | 34 | 35 |
    TargetLicense
    {{ target.file }}{{ target.license }}
    50 | 51 |
    52 | 53 | {% if not latest_targetlicense_list %} 54 |
    55 | No target bindings are available. 56 | {% endif %} 57 |
    58 | 59 |
    60 |
    61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 77 | 82 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | {% for lib in latest_liblicense_list %} 92 | 93 | 94 | 95 | 96 | 97 | {% endfor %} 98 |
    Library: 
    License: 
    {{ liblicenseform.library }}
    {{ liblicenseform.license }}
    76 | 78 | 81 | 83 | 84 |
    LibraryLicense
    {{ lib.library }}{{ lib.license }}
    99 | 100 |
    101 | 102 | {% if not latest_liblicense_list %} 103 |
    104 | No library bindings are available. 105 | {% endif %} 106 |
    107 | 108 | {% endblock %} 109 | 110 | -------------------------------------------------------------------------------- /compliance/templates/linkage/licenses.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | 5 | 6 | 7 | {% endblock %} 8 | 9 | {% block content %} 10 |

    In this page, users create references to the various licenses used in 11 | their products to allow them to define policies around linkages made between 12 | binaries and libraries available under licenses. Because license lists may 13 | be imported from different sources with different naming conventions, one can 14 | create a list of possible equivalent strings (aliases) for a particular license. 15 | You can add additional aliases to the list for a license simply by entering more 16 | and using the "Add Aliases" button again. Alias names must be unique.

    17 |
    18 |
    19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {% for lic in latest_license_list %} 56 | 57 | 58 | 59 | 60 | 61 | 62 | {% endfor %} 63 |
    Long NameAbbreviationVersion
    43 | 45 | 47 |
    Long NameAbbreviationVersion
    {{ lic.longname }}{{ lic.license }}{{ lic.version }}
    64 | 65 |
    66 | {% if not latest_license_list %} 67 |
    68 | No licenses are available. 69 | {% endif %} 70 |
    71 |
    72 |
    73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 94 | 95 | 96 | 97 | 98 | 102 | 106 | 107 | 108 | {% for alias in latest_aliases_list %} 109 | 110 | 111 | 112 | 113 | 114 | {% endfor %} 115 |
    LicenseAliases
    {{ aliasesform.license }} 87 | {% autoescape off %} 88 | {% if errmsg %}{{errmsg}}
    {% endif %} 89 | {% for input in input_list %} 90 | {{ input }} 91 | {% endfor %} 92 | {% endautoescape %} 93 |
    101 | 103 | 105 |
    {{ alias.license }}{{ alias.alias }}
    116 | 117 | 118 | 119 |
    120 | {% if not latest_aliases_list %} 121 |
    122 | No aliases are available. 123 | {% endif %} 124 |
    125 | 126 | {% endblock %} 127 | -------------------------------------------------------------------------------- /compliance/templates/linkage/noscript.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /compliance/templates/linkage/policy.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 |
    10 |

    In this window setting, you will define the license combinations that you want 11 | the tool to flag as part of reporting linkage relationship.

    12 |

    As an example, if you want to be flagged when a binary licensed under A links 13 | statically to a library licensed under B, then you need to enter A under the 14 | "Target License" field, B under the "Dependency License" field, specify "Static" 15 | under relationship and click on "Add Policy".

    16 |

    Once you do that, anytime the tool comes across a binary licensed under license A 17 | that statically links to a library licensed under license B, you 18 | will be visually notified via the UI. You can delete and add these policies.

    19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% if show_rank %} 28 | 29 | {% endif %} 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {% if show_rank %} 40 | 41 | {% endif %} 42 | 43 | 44 | 45 | 46 | 50 | 51 | 55 | 56 | 57 | {% if show_rank %} 58 | 59 | {% endif %} 60 | 61 | 62 | 63 | {% for pol in latest_policy_list %} 64 | 65 | 66 | 67 | 68 | 69 | 70 | {% if show_rank %} 71 | 72 | {% endif %} 73 | 74 | 75 | 76 | {% endfor %} 77 |
    Target LicenseDependency LicenseRelationshipRankStatusDate
    {{ policyform.tlicense }}{{ policyform.dlicense }}{{ policyform.relationship }}{{ policyform.rank }}{{ policyform.status }}
    49 | 52 | 54 |
    {{ pol.tlicense }}{{ pol.dlicense }}{{ pol.relationship }}{{ pol.rank }}{% if pol.status %}{{ pol.status }}{% else %}U{% endif %}{{ pol.edit_date|date:"M d, Y H:i:s" }}
    78 | 79 | {% if not show_rank %} 80 |
    81 | {{ policyform.rank }} 82 |
    83 | {% endif %} 84 |
    85 | 86 | {% if not latest_policy_list %} 87 |
    88 | No policies are available. 89 | {% endif %} 90 | 91 | {% endblock %} 92 | 93 | -------------------------------------------------------------------------------- /compliance/templates/linkage/results.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | 5 | {% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% if not latest_test_list %} 10 |

    No tests are available.

    11 | {% else %} 12 |
    13 |
    14 | 15 | 16 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% for test in latest_test_list %} 33 | 34 | 35 | 36 | 37 | 45 | 46 | {% endfor %} 47 |
    19 | 23 |
    TargetDateInformation
    {{ test.target }}{{ test.test_date|date:"M d, Y H:i:s" }} 38 | Target Directory:{% if test.target_dir %}{{ test.target_dir }}{% else %}N/A{% endif %}
    39 | Recursion Level: {{ test.recursion }}   40 | Check Static Deps: {% if test.disable_static %}No{% else %}Yes{% endif %}
    41 | User: {{ test.user }}
    42 | Project: {{ test.project }}
    43 | Comments: {{ test.comments }} 44 |
    48 | 49 |
    50 | {% endif %} 51 | 52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /compliance/templates/linkage/settings.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | 3 | {% block content %} 4 | 5 | {% if info_message %}

    {{ info_message }}

    {% endif %} 6 | 7 |

    8 | In order to detect statically-linked libraries, the application needs 9 | to collect data from the installed libraries on your test system. 10 | If a button appears below, you can activate it to clear out and reload the 11 | static symbol data. If no button appears, there is a job in progress; its 12 | status should be displayed and updated. 13 |

    14 | 15 |
    16 |
    17 | 18 |
    19 | 20 | {% if last_staticdb_update %} 21 |

    The static database was last updated on {{ last_staticdb_update }}.

    22 | {% else %} 23 |

    The static database has never been loaded.

    24 | {% endif %} 25 | 26 |
    27 | 28 |
    29 | 30 |
    31 | 32 |

    33 | When reloading data, the paths listed below will be search for libraries 34 | to read. You can add or remove paths from the list, one per line. 35 |

    36 | 37 |
    38 | {{ search_path_form.dirlist }} 39 |
    40 |
    41 | 42 | {% endblock %} 43 | 44 | {% block scripts %} 45 | 46 | 75 | 76 | {% endblock %} 77 | -------------------------------------------------------------------------------- /compliance/templates/linkage/test.html: -------------------------------------------------------------------------------- 1 | {% extends "linkage/base.html" %} 2 | {% block header %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% endblock %} 11 | 12 | {% block content %} 13 | 14 |
     {% include "linkage/noscript.html" %}
    15 |
    16 | 17 |
    18 | 19 |
    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
    Identify dependencies for a specific target file in a
    target directory
    (search for the file):
    {{ testform.disable_static.label }}:{{ testform.disable_static }}
    32 | {{ testform.target_dir.label }}: 33 |
    (Top-level directory to start file search) 34 |
    {{ testform.target_dir }}
    {{ testform.recursion.label }}: 42 |
    (How deep to drill into the library dependencies) 43 |
    {{ testform.recursion }}
    {{ testform.target.label }}: 48 |
    (File or top-level directory to test) 49 |
    {{ testform.target }}
    {{ testform.user.label }}:{{ testform.user }}
    {{ testform.project.label }}: 61 |
    (Optional name of the project being tested) 62 |
    {{ testform.project }}
    {{ testform.comments.label }}: 67 |
    (Freeform field for any test comments for reporting) 68 |
    {{ testform.comments }}
    78 |
    79 | 80 | 83 |
    84 | 85 |
    86 | 87 | {% if not static_data %} 88 |

    89 | NOTE: The database contains no data about static symbols, and thus 90 | will be unable to find any possible static library dependencies that may 91 | exist. If you want to perform static as well as dynamic checks, please 92 | reload the static database. 93 |

    94 | {% endif %} 95 | 96 |
    97 | Subscribe to the mailing list
    98 | Get latest version from git
    99 | Report bugs and request features at bugzilla
    100 |
    101 | 102 | {% endblock %} 103 | 104 | {% block scripts %} 105 | 186 | 187 | {% endblock %} 188 | 189 | -------------------------------------------------------------------------------- /compliance/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include 2 | from django.conf import settings 3 | 4 | from django.contrib import admin 5 | admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | (r'^linkage/$', 'compliance.linkage.views.test'), 9 | (r'^linkage/test/$', 'compliance.linkage.views.test'), 10 | (r'^linkage/dirlist/$', 'compliance.linkage.views.dirlist'), 11 | (r'^linkage/documentation/$', 'compliance.linkage.views.documentation'), 12 | (r'^linkage/results/$', 'compliance.linkage.views.results'), 13 | (r'^linkage/licenses\/$', 'compliance.linkage.views.licenses'), 14 | (r'^linkage/lbindings/$', 'compliance.linkage.views.lbindings'), 15 | (r'^linkage/policy/$', 'compliance.linkage.views.policy'), 16 | (r'^linkage/settings/$', 'compliance.linkage.views.settings_form'), 17 | (r'^linkage/taskstatus/$', 'compliance.linkage.views.taskstatus'), 18 | (r'^linkage/(?P\d+)/detail/$', 'compliance.linkage.views.detail'), 19 | (r'^site_media/(?P.*)$', 'django.views.static.serve', 20 | {'document_root': settings.STATIC_DOC_ROOT}), 21 | (r'^admin/', include(admin.site.urls)), 22 | ) 23 | -------------------------------------------------------------------------------- /desktop/dep-checker.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Dependency Checker Tool 3 | GenericName=Deps Checker 4 | Comment=Analyse linkage dependencies for FOSS software 5 | Exec=/opt/linuxfoundation/bin/dep-checker.py start 6 | Icon=/opt/linuxfoundation/share/icons/hicolor/16x16/apps/lf_small.png 7 | Terminal=false 8 | TryExec=/opt/linuxfoundation/bin/dep-checker.py 9 | Type=Application 10 | MimeType= 11 | Categories=Development 12 | -------------------------------------------------------------------------------- /desktop/lf_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxfoundation/dep-checker/9082d5b68306ec9eb5aebdfcd502575441650834/desktop/lf_small.png -------------------------------------------------------------------------------- /doc/Contributing: -------------------------------------------------------------------------------- 1 | Any contribution submitted for inclusion in the Dependency Checker Tool must 2 | be signed by its author following the Developer's Certificate of Origin 1.1. 3 | 4 | By making a contribution to this project, I certify that: 5 | a) The contribution was created in whole or in part by me and I have the right 6 | to submit it under the open source license indicated in the file; or 7 | b) The contribution is based upon previous work that, to the best of my 8 | knowledge, is covered under an appropriate open source license and I have 9 | the right under that license to submit that work with modifications, 10 | whether created in whole or in part by me, under the same open source 11 | license (unless I am permitted to submit under a different license), as 12 | indicated in the file; or 13 | c) The contribution was provided directly to me by some other person who 14 | certified (a), (b) or (c) and I have not modified it. 15 | d) I understand and agree that this project and the contribution are public 16 | and that a record of the contribution (including all personal information 17 | I submit with it, including my sign-off) is maintained indefinitely and 18 | may be redistributed consistent with this project or the open source 19 | license(s) involved. 20 | 21 | Patches to the mailing list need to be signed as: 22 | 23 | Signed-off-by: 24 | 25 | Same thing applies for reviewers: 26 | 27 | Signed-off-by: 28 | 29 | and committer: 30 | 31 | Signed-off-by: 32 | 33 | -------------------------------------------------------------------------------- /doc/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Linux Foundation 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os, sys 3 | 4 | if __name__ == "__main__": 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "compliance.settings") 6 | 7 | from django.core.management import execute_from_command_line 8 | 9 | execute_from_command_line(sys.argv) 10 | -------------------------------------------------------------------------------- /package/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE=dep-checker 2 | # version defined in site_settings.py 3 | VERSION=$(shell grep gui_version ../compliance/linkage/site_settings.py | awk '{print $$3}' | sed 's|"||g') 4 | RELEASE=2 5 | 6 | # we can optionally bundle django 7 | DJANGO_VERSION=1.8.18 8 | DJANGO_URL=http://www.djangoproject.com/download/$(DJANGO_VERSION)/tarball/ 9 | ifdef WITH_DJANGO 10 | WITH_ARGS=--with django 11 | endif 12 | 13 | # Derive date string for daily snapshots 14 | ISO_DATE=$(shell date +"%Y%m%d") 15 | PWD=$(shell pwd) 16 | 17 | FULL_PACKAGE_NAME=$(PACKAGE)-$(VERSION) 18 | RPM_BINARY_NAME=$(FULL_PACKAGE_NAME)-$(RELEASE).$(RPM_BUILD_ARCH).rpm 19 | RPM_SOURCE_NAME=$(FULL_PACKAGE_NAME)-$(RELEASE).src.rpm 20 | 21 | SOURCE1 = $(PACKAGE)-$(VERSION).tar.gz 22 | SOURCE2 = Django-$(DJANGO_VERSION).tar.gz 23 | 24 | # Temporary build directory 25 | TMP_BUILD_DIR=/tmp/$(FULL_PACKAGE_NAME) 26 | 27 | # Handle different version generation for snapshots than for official builds 28 | # OFFICIAL_RELEASE should be set to the tag to extract from version control 29 | ifdef OFFICIAL_RELEASE 30 | VERSION_SUFFIX= 31 | EXPORT_TAG=$(OFFICIAL_RELEASE) 32 | else 33 | VERSION_SUFFIX=.$(ISO_DATE) 34 | EXPORT_TAG=HEAD 35 | endif 36 | 37 | # Determine whether to use rpm or rpmbuild to build the packages 38 | ifeq ($(wildcard /usr/bin/rpmbuild),) 39 | RPM_BUILD_CMD=rpm 40 | else 41 | RPM_BUILD_CMD=rpmbuild 42 | endif 43 | 44 | # Get RPM configuration information 45 | # NOTE THAT RPM_TMP_BUILD_DIR IS DELETED AFTER THE RPM BUILD IS COMPLETED 46 | # The rpmrc file translates targets where there are multiple choices per 47 | # architecture. On build, the derived RPM_BUILD_ARCH is given as the target 48 | RCFILELIST="/usr/lib/rpm/rpmrc:./rpmrc" 49 | RPM_TMP_BUILD_DIR=/var/tmp/rpm-build 50 | # noarch package 51 | RPM_BUILD_ARCH=noarch 52 | RPM_BINARY_DIR=$(RPM_TMP_BUILD_DIR)/RPMS/noarch 53 | #RPM_BUILD_ARCH=$(shell rpm --rcfile ${RCFILELIST} --eval=%{_target_cpu}) 54 | #RPM_BINARY_DIR=$(RPM_TMP_BUILD_DIR)/RPMS/$(RPM_BUILD_ARCH) 55 | RPM_SRPM_DIR=$(RPM_TMP_BUILD_DIR)/SRPMS 56 | 57 | # Default target 58 | ifndef BUILD_NO_DEB 59 | all: rpm_package deb_package 60 | else 61 | all: rpm_package 62 | endif 63 | 64 | clean: 65 | @rm -f *.rpm *.deb $(SOURCE1) $(SOURCE2) $(PACKAGE).spec 66 | @rm -rf $(PACKAGE)-$(VERSION) $(PACKAGE)-$(VERSION).orig 67 | 68 | tarball: $(SOURCE1) 69 | ifdef WITH_DJANGO 70 | $(shell if [ ! -f $(SOURCE2) ];then wget $(DJANGO_URL);fi) 71 | endif 72 | 73 | # Specfile generation rule 74 | %.spec : %.spec.sed 75 | sed -e "s#@VERSION@#`echo $(VERSION)`#" -e "s#@RELEASE@#`echo $(RELEASE)`#" -e "s#@DJANGO_VERSION@#`echo $(DJANGO_VERSION)`#" < $< > $@ 76 | 77 | deb_package: rpm_package 78 | alien -gcdk $(RPM_BINARY_NAME) 79 | perl -pe 's/^(Depends:.*)/\1, python-django/' < $(PACKAGE)-$(VERSION)/debian/control > $(PACKAGE)-$(VERSION)/debian/control.new 80 | rm -f $(PACKAGE)-$(VERSION)/debian/control 81 | cd $(PACKAGE)-$(VERSION)/debian && mv control.new control 82 | cd $(PACKAGE)-$(VERSION) && fakeroot debian/rules binary 83 | rm -rf $(PACKAGE)-$(VERSION) $(PACKAGE)-$(VERSION).orig 84 | 85 | rpm_package: $(RPM_BINARY_NAME) $(RPM_SOURCE_NAME) 86 | 87 | list_uploadable: 88 | @echo $(RPM_BINARY_NAME) 89 | ifndef BUILD_NO_DEB 90 | @ls *.deb 91 | endif 92 | 93 | $(RPM_BINARY_NAME) $(RPM_SOURCE_NAME): $(PACKAGE).spec tarball 94 | @mkdir -p $(RPM_TMP_BUILD_DIR)/BUILD 95 | @mkdir -p $(RPM_TMP_BUILD_DIR)/RPMS 96 | @mkdir -p $(RPM_TMP_BUILD_DIR)/SRPMS 97 | ifdef SIGN_PACKAGES 98 | @expect -c 'set timeout -1' -c 'spawn $(RPM_BUILD_CMD) --sign --rcfile ${RCFILELIST} --define=_sourcedir\ $(PWD) --define=_topdir\ $(RPM_TMP_BUILD_DIR) --define=_target_cpu\ $(RPM_BUILD_ARCH) $(WITH_ARGS) -ba $(PACKAGE).spec' -c 'expect -ex "Enter pass phrase:"' -c 'send "\n"' -c 'expect "Executing(%clean)"' -c 'expect "exit 0"' -c 'send "\n"' 99 | else 100 | @$(RPM_BUILD_CMD) --rcfile ${RCFILELIST} --define="_sourcedir $(PWD)" --define="_topdir $(RPM_TMP_BUILD_DIR)" --define="_target_cpu $(RPM_BUILD_ARCH)" $(WITH_ARGS) -ba $(PACKAGE).spec 101 | endif 102 | @mv $(RPM_SRPM_DIR)/$(RPM_SOURCE_NAME) . 103 | @mv $(RPM_BINARY_DIR)/$(RPM_BINARY_NAME) . 104 | @rm -rf $(RPM_TMP_BUILD_DIR) 105 | 106 | $(SOURCE1): 107 | (cd .. && git archive --format=tar --prefix=$(PACKAGE)-$(VERSION)/ $(EXPORT_TAG)) \ 108 | | gzip -9 > $@ 109 | 110 | .PHONY : tarball rpm_package 111 | -------------------------------------------------------------------------------- /package/dep-checker.spec.sed: -------------------------------------------------------------------------------- 1 | %define ver @VERSION@ 2 | %define rel @RELEASE@ 3 | %define django_ver @DJANGO_VERSION@ 4 | 5 | %define basedir /opt/linuxfoundation 6 | 7 | # optionally bundle django 8 | %define bundle_django 0 9 | %{?_with_django: %{expand: %%global bundle_django 1}} 10 | %{?_without_django: %{expand: %%global bundle_django 0}} 11 | %if !%bundle_django 12 | %endif 13 | 14 | # %{version}, %{rel} are provided by the Makefile 15 | Summary: Dependency Checker Tool 16 | Name: dep-checker 17 | Version: %{ver} 18 | Release: %{rel} 19 | License: LF 20 | Group: Development/Tools 21 | Source: %{name}-%{version}.tar.gz 22 | %if %bundle_django 23 | Source1: Django-%{django_ver}.tar.gz 24 | %endif 25 | URL: http://git.linux-foundation.org/dep-checker.git 26 | BuildRoot: %{_tmppath}/%{name}-root 27 | AutoReqProv: no 28 | %if !%bundle_django 29 | Requires: python-django >= 1.2 30 | %endif 31 | BuildRequires: python w3m 32 | 33 | %description 34 | A compliance tool to explore FOSS dependencies in binaries/libraries 35 | 36 | If you don't get a menu entry, run the app with: 37 | %{basedir}/bin/dep-checker.py start 38 | 39 | If a browser window or tab doesn't open, goto: 40 | http://127.0.0.1:8000/linkage 41 | 42 | The command-line tool is: 43 | %{basedir}/readelf.py 44 | 45 | Note: This package can be built with django bundled, using '--with django' 46 | 47 | #================================================== 48 | %prep 49 | %setup -q 50 | 51 | #================================================== 52 | %build 53 | make 54 | 55 | #================================================== 56 | %install 57 | 58 | rm -rf ${RPM_BUILD_ROOT} 59 | install -d ${RPM_BUILD_ROOT}%{basedir} 60 | cp -ar bin ${RPM_BUILD_ROOT}%{basedir} 61 | cp -ar compliance ${RPM_BUILD_ROOT}%{basedir} 62 | find ${RPM_BUILD_ROOT}%{basedir} -name '*.pyc' | xargs rm -f 63 | rm -f ${RPM_BUILD_ROOT}%{basedir}/compliance/media/docs/* 64 | install -m 644 compliance/media/docs/*.html ${RPM_BUILD_ROOT}%{basedir}/compliance/media/docs 65 | install -d ${RPM_BUILD_ROOT}%{basedir}/share/icons/hicolor/16x16/apps 66 | install -m 644 desktop/lf_small.png ${RPM_BUILD_ROOT}%{basedir}/share/icons/hicolor/16x16/apps 67 | install -d ${RPM_BUILD_ROOT}%{basedir}/share/applications 68 | install -m 644 desktop/%{name}.desktop ${RPM_BUILD_ROOT}%{basedir}/share/applications 69 | install -d ${RPM_BUILD_ROOT}%{basedir}/doc/%{name} 70 | install -m 644 doc/LICENSE doc/Contributing ${RPM_BUILD_ROOT}%{basedir}/doc/%{name} 71 | install -m 644 AUTHORS Changelog README.txt README.apache-mod_wsgi ${RPM_BUILD_ROOT}%{basedir}/doc/%{name} 72 | install -d ${RPM_BUILD_ROOT}/var%{basedir}/log/compliance 73 | %if %bundle_django 74 | tar -xf %{SOURCE1} 75 | cp -ar Django-%{django_ver}/django ${RPM_BUILD_ROOT}%{basedir}/compliance 76 | %endif 77 | 78 | #================================================== 79 | %clean 80 | if [ -z "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ]; then 81 | rm -rf ${RPM_BUILD_ROOT} 82 | fi 83 | 84 | #================================================== 85 | %pre 86 | PATH=/usr/sbin:$PATH 87 | groupadd compliance >/dev/null 2>&1 || true 88 | 89 | id compliance >/dev/null 2>&1 90 | 91 | if [ $? -ne 0 ]; then 92 | useradd -d /home/compliance -s /bin/sh -p "" -c "compliance tester login" compliance -m -g compliance >/dev/null 2>&1 93 | 94 | if [ $? = 0 ]; then 95 | echo 96 | else 97 | echo "Failed to add user 'compliance'." 98 | echo "To be able to run the tests you should add this user manually." 99 | exit 1 100 | fi 101 | fi 102 | 103 | %post 104 | if [ -x /usr/bin/xdg-desktop-menu ];then 105 | xdg-desktop-menu install /opt/linuxfoundation/share/applications/dep-checker.desktop 106 | fi 107 | %if %bundle_django 108 | if [ ! -e %{basedir}/bin/django ];then 109 | cd %{basedir}/bin 110 | ln -sf ../compliance/django . 111 | fi 112 | %endif 113 | 114 | %preun 115 | if [ -x /usr/bin/xdg-desktop-menu ];then 116 | xdg-desktop-menu uninstall /opt/linuxfoundation/share/applications/dep-checker.desktop 117 | fi 118 | 119 | %postun 120 | # don't mess with things on an upgrade, or if janitor is installed 121 | if [ "$1" = "0" -a ! -f %{basedir}/bin/code-janitor.py ];then 122 | TESTER=compliance 123 | id $TESTER > /dev/null 2>/dev/null 124 | if [ $? -eq 0 ]; then 125 | userdel -r $TESTER > /dev/null 2>/dev/null 126 | if [ $? = 0 ]; then 127 | echo "User '$TESTER' was successfully deleted" 128 | else 129 | echo "Warning: failed to delete user '$TESTER'" 130 | fi 131 | fi 132 | 133 | TESTGROUP=compliance 134 | cat /etc/group | grep ^$TESTGROUP: > /dev/null 2>/dev/null 135 | if [ $? -eq 0 ]; then 136 | groupdel $TESTGROUP > /dev/null 2>/dev/null 137 | if [ $? = 0 ]; then 138 | echo "Group '$TESTGROUP' was successfully deleted." 139 | else 140 | echo "Warning: failed to delete group '$TESTGROUP'." 141 | fi 142 | fi 143 | %if %bundle_django 144 | if [ -d %{basedir}/janitor/django ];then 145 | cd %{basedir}/bin 146 | ln -sf ../janitor/django . 147 | fi 148 | %endif 149 | fi 150 | 151 | #================================================== 152 | %files 153 | %defattr(-,compliance,compliance) 154 | 155 | %dir %{basedir}/bin 156 | %dir %{basedir}/doc/%{name} 157 | %dir %{basedir}/compliance 158 | %dir %{basedir}/share/applications 159 | %dir %{basedir}/share/icons/hicolor/16x16/apps 160 | %dir /var/%{basedir}/log/compliance 161 | 162 | %{basedir}/bin/* 163 | %{basedir}/compliance/* 164 | %{basedir}/share/icons/hicolor/16x16/apps/* 165 | %{basedir}/share/applications/* 166 | %doc %{basedir}/doc/%{name}/* 167 | 168 | %changelog 169 | * Fri Jun 25 2010 Jeff Licquia 170 | - move static checking db into the regular compliance db 171 | 172 | * Thu Jun 24 2010 Jeff Licquia 173 | - switch to git from bzr for version control 174 | 175 | * Mon Jun 07 2010 Jeff Licquia 176 | - add static checking database 177 | 178 | * Fri Jun 04 2010 Stew Benedict 179 | - fix compliance user/group setup for upgrade, v0.0.3 180 | - add README.html s/Licence/License/ 181 | 182 | * Wed Jun 02 2010 Stew Benedict 183 | - initial packaging 184 | -------------------------------------------------------------------------------- /package/rpmmacros: -------------------------------------------------------------------------------- 1 | # overrrides for LSB packaging 2 | # 3 | # Should unpackaged files in a build root terminate a build? 4 | # 5 | # 0 for legacy compatibility. 6 | %_unpackaged_files_terminate_build 0 7 | 8 | # Should missing %doc files in the build directory terminate a build? 9 | # 10 | # 0 for legacy compatibility. 11 | %_missing_doc_files_terminate_build 0 12 | 13 | # Force gzip payload 14 | %_binary_payload w9.gzdio 15 | -------------------------------------------------------------------------------- /package/rpmrc: -------------------------------------------------------------------------------- 1 | macrofiles: /usr/lib/rpm/macros:~/.rpmmacros:rpmmacros 2 | # 3 | # Force a 486 target 4 | buildarchtranslate: i686 i486 5 | buildarchtranslate: i586 i486 6 | buildarchtranslate: i386 i486 7 | buildarchtranslate: pentium3 i486 8 | buildarchtranslate: pentium4 i486 9 | buildarchtranslate: athlon i486 10 | buildarchtranslate: k6 i486 11 | 12 | # Force a ppc64 target 13 | buildarchtranslate: ppc64pseries ppc64 14 | buildarchtranslate: ppc64iseries ppc64 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import find_packages, setup 3 | 4 | from compliance.linkage import site_settings 5 | 6 | with open(os.path.join(os.path.dirname(__file__), '00README')) as readme: 7 | README = readme.read() 8 | 9 | # allow setup.py to be run from any path 10 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 11 | 12 | # docs build/cleanup 13 | os.system("cd compliance/media/docs && make") 14 | 15 | setup( 16 | name='dep-checker', 17 | version=site_settings.gui_version, 18 | packages=find_packages(), 19 | scripts=['manage.py', 'bin/dep-checker.py', 'bin/readelf.py'], 20 | install_requires=['Django>=1.8,<1.9'], 21 | include_package_data=True, 22 | license='BSD License', # example license 23 | description='A framework for finding license dependencies in binary applications.', 24 | long_description=README, 25 | url='https://compliance.linuxfoundation.org/', 26 | author='Jeff Licquia', 27 | author_email='licquia@linuxfoundation.org', 28 | classifiers=[ 29 | 'Environment :: Web Environment', 30 | 'Framework :: Django', 31 | 'Framework :: Django :: 1.8', # replace "X.Y" as appropriate 32 | 'Intended Audience :: Developers', 33 | 'License :: OSI Approved :: BSD License', # example license 34 | 'Operating System :: OS Independent', 35 | 'Programming Language :: Python', 36 | # Replace these appropriately if you are stuck on Python 2. 37 | 'Programming Language :: Python :: 2', 38 | 'Programming Language :: Python :: 2.7', 39 | 'Topic :: Internet :: WWW/HTTP', 40 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 41 | ], 42 | ) 43 | --------------------------------------------------------------------------------