├── .gitignore ├── misc ├── uprocd.policy ├── cgrmvd.service ├── uprocd@.service └── com.refi64.uprocd.Cgrmvd.conf ├── .gitmodules ├── scripts ├── push_web └── generate_modules.py ├── modules ├── ruby │ ├── ruby.module │ ├── _uprocd_requires.rb │ └── ruby.c ├── derived │ └── derived.module ├── python │ ├── mypy.module │ ├── python.module │ ├── ipython.module │ ├── mrkd.module │ ├── python.c │ └── _uprocd_modules.py └── simple │ ├── simple.module │ └── simple.c ├── man ├── uprocd_module_path_free.3.md ├── uprocd_context_free.3.md ├── uprocd_config_present.3.md ├── ruby.module.7.md ├── uprocd_context_get_cwd.3.md ├── uprocd_on_exit.3.md ├── uprocd.index.7.md ├── uprocd_module_directory.3.md ├── python.module.7.md ├── uprocd_context_enter.3.md ├── uprocd_context_get_args.3.md ├── uprocd_context_get_env.3.md ├── uprocd_module_path.3.md ├── cgrmvd.policy.5.md ├── uprocd_config_string.3.md ├── uprocd_config_list_size.3.md ├── uprocd_config_number.3.md ├── uprocd_config_string_at.3.md ├── index.ini ├── uprocd_config_number_at.3.md ├── uprocd_module_entry.3.md ├── uprocd_run.3.md ├── uprocctl.1.md ├── cgrmvd.7.md ├── uprocd.7.md ├── uprocd.h.3.md └── uprocd.module.5.md ├── README.md ├── web ├── man-extra.css └── index.html ├── src ├── common │ ├── common.h │ └── common.c ├── uprocd │ ├── private.h │ ├── config.c │ ├── bus.c │ ├── config-parse.c │ ├── main.c │ └── api.c ├── cgrmvd │ └── cgrmvd.c └── uprocctl │ └── main.c ├── api └── uprocd.h ├── LICENSE-THIRD-PARTY ├── fbuildroot.py └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | __pycache__/ 3 | .web/ 4 | -------------------------------------------------------------------------------- /misc/uprocd.policy: -------------------------------------------------------------------------------- 1 | /usr/share/uprocd/bin/uprocd : /usr/bin/uprocctl 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sds"] 2 | path = sds 3 | url = https://github.com/antirez/sds.git 4 | -------------------------------------------------------------------------------- /misc/cgrmvd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=CGRoup MoVer Daemon 3 | 4 | [Service] 5 | Type=dbus 6 | BusName=com.refi64.uprocd.Cgrmvd 7 | ExecStart=/usr/share/uprocd/bin/cgrmvd 8 | KillMode=process 9 | Restart=always 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /misc/uprocd@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=The uprocd daemon for %I 3 | 4 | [Service] 5 | Type=dbus 6 | BusName=com.refi64.uprocd.modules.%I 7 | ExecStart=/usr/share/uprocd/bin/uprocd + "%I" 8 | KillMode=process 9 | Restart=always 10 | 11 | [Install] 12 | WantedBy=default.target 13 | -------------------------------------------------------------------------------- /scripts/push_web: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "`dirname "$0"`/.." 4 | [ -d .web ] || git clone --depth=1 -b gh-pages https://github.com/kirbyfan64/uprocd.git .web 5 | fbuild 6 | cd .web 7 | rm -rf * 8 | cp -r ../build/web/* . 9 | git add . 10 | git commit -am "deploy on `date '+%Y-%m-%d-%H:%M:%S-UTC:%z'`" 11 | git push -u origin gh-pages 12 | -------------------------------------------------------------------------------- /modules/ruby/ruby.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [NativeModule] 6 | 7 | [Properties] 8 | Preload=string 9 | Run=string 10 | 11 | [Defaults] 12 | Preload= 13 | Run= 14 | -------------------------------------------------------------------------------- /modules/derived/derived.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [DerivedModule] 6 | Base=simple 7 | String=simple 8 | stuff 9 | is 10 | 11 | awesome 12 | NumberList=1 2 3 13 | -------------------------------------------------------------------------------- /modules/python/mypy.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [DerivedModule] 6 | Base=python 7 | Preload=import mypy.main 8 | Run= 9 | from mypy.main import main 10 | main(None) 11 | -------------------------------------------------------------------------------- /modules/python/python.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [NativeModule] 6 | 7 | [Properties] 8 | Preload=string 9 | Run=string 10 | 11 | [Defaults] 12 | Preload= 13 | Run= 14 | -------------------------------------------------------------------------------- /modules/ruby/_uprocd_requires.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | $:.each do |path| 4 | if path.include? "/ruby/#{RUBY_VERSION}" 5 | paths = Pathname.glob("#{path}/*.{rb,so}") 6 | paths 7 | .map{|p| p.basename.sub_ext ''} 8 | .map(&:to_s) 9 | .select{|req| !['continuation', 'debug', 'profile'].include? req } 10 | .each{|req| require req} 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /misc/com.refi64.uprocd.Cgrmvd.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /man/uprocd_module_path_free.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_module_path_free -- Free a uprocd_module_path return value 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT void uprocd_module_path_free(char *path); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will free the string returned from uprocd_module_path(3). 14 | 15 | ## SEE ALSO 16 | 17 | uprocd.index(7), uprocd.h(3), uprocd_module_directory(3), uprocd_module_path(3) 18 | -------------------------------------------------------------------------------- /man/uprocd_context_free.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_context_free -- Free a uprocd context 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT void uprocd_context_free(uprocd_context *ctx); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | Frees the memory associated with the given context. Any values previously retrieved 14 | from the context will become invalidated. 15 | 16 | ## SEE ALSO 17 | 18 | uprocd.index(7), uprocd.h(3), uprocd_module_entry(3) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uprocd 2 | 3 | ## What is uprocd? 4 | 5 | uprocd is a "process preloader" or "process cache" for Linux; it starts processes ahead 6 | of time in the background, then brings forked copies to the foreground when you request 7 | them. This means that running a command through uprocd will usually be faster than 8 | just running it normally, since it's already been initialized. 9 | 10 | ## Want to learn more? 11 | 12 | [Check out the website!](https://refi64.com/uprocd) 13 | -------------------------------------------------------------------------------- /man/uprocd_config_present.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_present -- Determine if the given property is present 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT int uprocd_config_present(const char *key); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will check if the given key is present in the module's properties. 14 | 15 | ## RETURN VALUE 16 | 17 | 1 if it is present, 0 if it isn't. 18 | 19 | ## SEE ALSO 20 | 21 | uprocd.index(7), uprocd.h(3) 22 | -------------------------------------------------------------------------------- /modules/python/ipython.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [DerivedModule] 6 | Base=python 7 | Preload=import IPython 8 | Run= 9 | from IPython.terminal.interactiveshell import TerminalInteractiveShell 10 | from IPython import start_ipython 11 | 12 | TerminalInteractiveShell.simple_prompt = False 13 | sys.exit(start_ipython()) 14 | -------------------------------------------------------------------------------- /modules/simple/simple.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [NativeModule] 6 | NativeLib=simple 7 | ProcessName=simple 8 | 9 | [Properties] 10 | String=string 11 | Number=number 12 | StringList=list string 13 | NumberList=list number 14 | 15 | [Defaults] 16 | String=simple 17 | Number=2 18 | StringList=string list 19 | NumberList=1 2 3 20 | -------------------------------------------------------------------------------- /modules/python/mrkd.module: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | 5 | [DerivedModule] 6 | Base=python 7 | Preload= 8 | import sys 9 | sys.argv = [] 10 | import mrkd 11 | Run= 12 | import mrkd, plac, sys 13 | sys.argv[0] = 'mrkd' 14 | 15 | orig = plac.call 16 | def call(f): orig(f, sys.argv[1:]) 17 | plac.call = call 18 | mrkd.main() 19 | -------------------------------------------------------------------------------- /man/ruby.module.7.md: -------------------------------------------------------------------------------- 1 | # ruby.module -- The uprocd Ruby module 2 | 3 | ## SYNOPSIS 4 | 5 | ruby.module 6 | 7 | ## DESCRIPTION 8 | 9 | This is the Ruby native module for uprocd. 10 | 11 | ## PROPERTIES 12 | 13 | **Preload=** 14 | 15 | Code to run during the initialization phase. 16 | 17 | **Run=** 18 | 19 | Code to run when the module is forked. 20 | 21 | ## EXAMPLE 22 | 23 | ```ini 24 | [DerivedModule] 25 | Base=ruby 26 | # This code is to initialize the module. 27 | Preload=require 'time' 28 | # This will be called when the module is run. 29 | Run= 30 | puts "Running code..." 31 | ``` 32 | -------------------------------------------------------------------------------- /man/uprocd_context_get_cwd.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_context_get_cwd -- Retrieve the working directory from a uprocd context 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT const char * uprocd_context_get_cwd(uprocd_context *ctx) 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will return the working directory of the uprocctl(1) process that 14 | called this module. 15 | 16 | ## EXAMPLE 17 | 18 | ```c 19 | const char *cwd = uprocd_context_get_cwd(ctx); 20 | printf("uprocctl was called from %s\n", cwd); 21 | ``` 22 | 23 | ## SEE ALSO 24 | 25 | uprocd.index(7), uprocd.h(3), uprocctl(1) 26 | -------------------------------------------------------------------------------- /man/uprocd_on_exit.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_on_exit -- Set a handler to be called on uprocd_run failure 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | typedef void (*uprocd_exit_handler)(void *userdata); 9 | UPROCD_EXPORT void uprocd_on_exit(uprocd_exit_handler func, void *userdata); 10 | ``` 11 | 12 | ## DESCRIPTION 13 | 14 | This function will set a handler to be called if uprocd_run(3) fails. This handler 15 | will only ever be called from the original parent process. 16 | 17 | The given userdata will be passed to the handler when it is called. 18 | 19 | ## SEE ALSO 20 | 21 | uprocd.index(7), uprocd.h(3), uprocd_run(3) 22 | -------------------------------------------------------------------------------- /man/uprocd.index.7.md: -------------------------------------------------------------------------------- 1 | # uprocd.index -- uprocd documentation index 2 | 3 | ## RECOMMENDATIONS 4 | 5 | If you're just starting out, read uprocd(7) and uprocctl(1), which should send you 6 | well on your way. 7 | 8 | ## BASIC USAGE 9 | 10 | uprocd(7) - Guide on managing uprocd modules with systemd 11 | 12 | uprocctl(1), u(1) - Query or run uprocd modules 13 | 14 | ## DEVELOPING MODULES 15 | 16 | uprocd.module(5) - uprocd module configuration format 17 | 18 | uprocd.h(3) - uprocd native module API index 19 | 20 | ## CGRMVD 21 | 22 | cgrmvd(7) - The CGRoup MoVer Daemon 23 | 24 | cgrmvd.policy(5) - cgrmvd policy configuration format 25 | -------------------------------------------------------------------------------- /man/uprocd_module_directory.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_module_directory -- Retrieve the path to the current uprocd module 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT const char * uprocd_module_directory(); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function returns the directory holding the current module's .module file. If you 14 | wish to retrieve the path to a file within this directory, you can use 15 | uprocd_module_path(3) as a shortcut. 16 | 17 | ## RETURN VALUE 18 | 19 | The resulting path. This function never fails. 20 | 21 | ## SEE ALSO 22 | 23 | uprocd.index(7), uprocd.h(3), uprocd.module(5), uprocd_module_path(3) 24 | -------------------------------------------------------------------------------- /man/python.module.7.md: -------------------------------------------------------------------------------- 1 | # python.module -- The uprocd Python module 2 | 3 | ## SYNOPSIS 4 | 5 | python.module 6 | 7 | ## DESCRIPTION 8 | 9 | This is the Python native module for uprocd. 10 | 11 | ## PROPERTIES 12 | 13 | **Preload=** 14 | 15 | Code to run during the initialization phase. 16 | 17 | **Run=** 18 | 19 | Code to run when the module is forked. 20 | 21 | ## EXAMPLE 22 | 23 | ```ini 24 | [DerivedModule] 25 | Base=python 26 | # This code is to initialize the module. 27 | Preload=import IPython 28 | # This will be called when the module is run. 29 | Run= 30 | from IPython import start_ipython 31 | sys.exit(start_ipython()) 32 | ``` 33 | -------------------------------------------------------------------------------- /man/uprocd_context_enter.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_context_enter -- Enter a uprocd context object 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT void uprocd_context_enter(uprocd_context *ctx); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will "enter" the given context. The following operations will be 14 | performed: 15 | 16 | 1. The current environment will be matched to the context's environment. 17 | 2. The current working directory will be changed to the context's working directory. 18 | 3. The current process will be attached to the context's standard I/O and terminal. 19 | 20 | ## SEE ALSO 21 | 22 | uprocd.index(7), uprocd.h(3), uprocd(7) 23 | -------------------------------------------------------------------------------- /man/uprocd_context_get_args.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_context_get_args -- Retrieve arguments from a uprocd context 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT void uprocd_context_get_args(uprocd_context *ctx, int *pargc, 9 | char ***pargv); 10 | ``` 11 | 12 | ## DESCRIPTION 13 | 14 | This function will retrieve the command-line arguments that were passed to 15 | uprocctl(1) and then stored in the context. The argument count will be stored in 16 | pargc, and the argument array will be stored in pargv. 17 | 18 | ## EXAMPLE 19 | 20 | ```c 21 | uprocd_context *ctx = uprocd_run(); 22 | 23 | int argc; 24 | char **argv; 25 | 26 | uprocd_context_get_args(ctx, &argc, &argv); 27 | ``` 28 | 29 | ## SEE ALSO 30 | 31 | uprocd.index(7), uprocd.h(3), uprocctl(1) 32 | -------------------------------------------------------------------------------- /man/uprocd_context_get_env.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_context_get_env -- Retrieve the environment from a uprocd context 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT const char ** uprocd_context_get_env(uprocd_context *ctx); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This functions will retrieve the environment of the uprocctl(1) process that called 14 | this module. 15 | 16 | ## RETURN VALUE 17 | 18 | A null-terminated array of strings. Each even index is an environment key, and each 19 | even value is the corresponding value. 20 | 21 | ## EXAMPLE 22 | 23 | ```c 24 | const char **env = uprocd_context_get_env(ctx); 25 | for (const char **p = env; *p != NULL; p += 2) { 26 | const char *key = p[0], *value = p[1]; 27 | printf("%s=%s\n", key, value); 28 | } 29 | ``` 30 | 31 | ## SEE ALSO 32 | 33 | uprocd.index(7), uprocd.h(3), uprocctl(1) 34 | -------------------------------------------------------------------------------- /man/uprocd_module_path.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_module_path -- Retrieve the path to a file next to the current uprocd module 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT char * uprocd_module_path(const char *path); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the directory containing the current module's .module file, 14 | then it will append the given path to that directory and return the result. It is 15 | roughly equivalent to this: 16 | 17 | ```c 18 | // Same as uprocd_module_path("some-file") 19 | char *path = "some-file"; 20 | char *directory = uprocd_module_directory(); 21 | char full_path[1024]; 22 | sprintf(full_path, "%s/%s", directory, path); 23 | // full_path is the result. 24 | ``` 25 | 26 | The return value must be freed using uprocd_module_path_free(3). 27 | 28 | ## RETURN VALUE 29 | 30 | The resulting path. This function never fails. 31 | 32 | ## SEE ALSO 33 | 34 | uprocd.index(7), uprocd.h(3), uprocd_module_directory(3), uprocd_module_path_free(3) 35 | -------------------------------------------------------------------------------- /man/cgrmvd.policy.5.md: -------------------------------------------------------------------------------- 1 | # cgrmvd.policy -- cgrmvd policy configuration format 2 | 3 | ## SYNOPSIS 4 | 5 | policy.policy 6 | 7 | ## DESCRIPTION 8 | 9 | A cgrmvd policy is a simple configuration file ending in .policy. 10 | 11 | ## SYNTAX 12 | 13 | Each line of the file must be either empty, a comment, or a policy. 14 | 15 | Comments are lines that being with a pound sign (**#**). 16 | 17 | Policy lines follow this format: 18 | 19 | ``` 20 | copier : origin1 origin2 origin3... 21 | ``` 22 | 23 | The colon in the middle must be surrounded by whitespace on both sides. 24 | 25 | ## EXAMPLE 26 | 27 | ``` 28 | # This policy file declares that /usr/bin/foo can be moved to the cgroups of either 29 | # /usr/bin/bar or /usr/bin/baz. 30 | # Notice the format: copier : origin1 origin2 origin3... 31 | /usr/bin/foo : /usr/bin/bar /usr/bin/baz 32 | ``` 33 | 34 | ``` 35 | # This is the policy file uprocd uses. It declares that uprocd can be moved to the 36 | # cgroups of uprocctl processes. 37 | /usr/share/uprocd/bin/uprocd : /usr/bin/uprocctl 38 | ``` 39 | -------------------------------------------------------------------------------- /scripts/generate_modules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | from bs4 import BeautifulSoup 8 | import requests 9 | import re 10 | 11 | req = requests.get('https://docs.python.org/3/library/index.html') 12 | tree = BeautifulSoup(req.text, 'html.parser') 13 | 14 | passed_first = False 15 | ignore = re.compile(r'(?:^import\b|with\b|^_|[.]|\(\)$)') 16 | 17 | with open('modules/python/_uprocd_modules.py', 'w') as fp: 18 | for node in tree.select('a.reference.internal code.literal span.pre'): 19 | module = node.contents[0] 20 | if module == 'string': 21 | passed_first = True 22 | elif not passed_first or ignore.search(module) or module == 'import': 23 | continue 24 | 25 | fp.write(''' 26 | try: 27 | import {module} 28 | except Exception as ex: 29 | print('{module}', ex) 30 | '''.format(module=module)) 31 | -------------------------------------------------------------------------------- /man/uprocd_config_string.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_string -- Return the string at the given property 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT const char * uprocd_config_string(const char *key); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the string at the given property. To retrieve a string from 14 | a list, use uprocd_config_string_at(3). 15 | 16 | ## RETURN VALUE 17 | 18 | The string value, or NULL if it is not present. It is undefined to call this function 19 | on a non-string value. 20 | 21 | ## EXAMPLE 22 | 23 | Module config: 24 | 25 | ```ini 26 | [NativeModule] 27 | 28 | [Properties] 29 | Present=string 30 | NotPresent=string 31 | 32 | [Defaults] 33 | Present=test 34 | ``` 35 | 36 | Source code: 37 | 38 | ```c 39 | const char *s; 40 | 41 | s = uprocd_config_string("Present"); 42 | printf("Present: %s\n", s); // Present: test 43 | 44 | s = uprocd_config_string("NotPresent"); 45 | printf("NotPresent: %s\n", s); // NotPresent: (NULL) (if your libc doesn't crash) 46 | ``` 47 | 48 | ## SEE ALSO 49 | 50 | uprocd.index(7), uprocd.h(3), uprocd_config_string_at(3) 51 | -------------------------------------------------------------------------------- /man/uprocd_config_list_size.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_list_size -- Return the size of the list at the given property 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT int uprocd_config_list_size(const char *key); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the size of the list at the given key in the module's 14 | properties. 15 | 16 | ## RETURN VALUE 17 | 18 | The list's length, or -1 if it is not present. It is undefined to call this function 19 | on a non-list value. 20 | 21 | ## EXAMPLE 22 | 23 | Module config: 24 | 25 | ```ini 26 | [NativeModule] 27 | 28 | [Properties] 29 | Present=list string 30 | NotPresent=list string 31 | 32 | [Defaults] 33 | Present=a b c 34 | ``` 35 | 36 | Source code: 37 | 38 | ```c 39 | int len; 40 | 41 | len = uprocd_config_list_size("Present"); 42 | printf("Size of Present: %d\n", len); // Size of Present: 3 43 | 44 | len = uprocd_config_list_size("NotPresent"); 45 | printf("Size of NotPresent: %d\n", len); // Size of NotPresent: -1 46 | ``` 47 | 48 | ## SEE ALSO 49 | 50 | uprocd.index(7), uprocd.h(3), uprocd_config_string_at(3), uprocd_config_number_at(3) 51 | -------------------------------------------------------------------------------- /modules/simple/simple.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "uprocd.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | void sig(int s) { 12 | puts("Got signal"); 13 | } 14 | 15 | UPROCD_EXPORT void uprocd_module_entry() { 16 | puts("Inside uprocd_module_entry"); 17 | uprocd_context *ctx = uprocd_run(); 18 | puts("Got uprocd_context..."); 19 | uprocd_context_enter(ctx); 20 | puts("Entered context!"); 21 | 22 | printf("String: %s\n", uprocd_config_string("String")); 23 | printf("Number: %f\n", uprocd_config_number("Number")); 24 | 25 | for (int i = 0; i < uprocd_config_list_size("StringList"); i++) { 26 | printf("StringList %s\n", uprocd_config_string_at("StringList", i)); 27 | } 28 | 29 | for (int i = 0; i < uprocd_config_list_size("NumberList"); i++) { 30 | printf("NumberList %f\n", uprocd_config_number_at("NumberList", i)); 31 | } 32 | 33 | signal(SIGINT, sig); 34 | sleep(10); 35 | puts("Finished sleep!"); 36 | } 37 | -------------------------------------------------------------------------------- /man/uprocd_config_number.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_number -- Return the number at the given property 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT double uprocd_config_number(const char *key); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the number at the given property. To retrieve a number from 14 | a list, use uprocd_config_number_at(3). 15 | 16 | ## RETURN VALUE 17 | 18 | The number's value, or 0 if it is not present. It is undefined to call this function 19 | on a non-number value. 20 | 21 | To distinguist between 0 values and non-present properties, use 22 | uprocd_config_present(3). 23 | 24 | ## EXAMPLE 25 | 26 | Module config: 27 | 28 | ```ini 29 | [NativeModule] 30 | 31 | [Properties] 32 | Present=number 33 | NotPresent=number 34 | 35 | [Defaults] 36 | Present=10 37 | ``` 38 | 39 | Source code: 40 | 41 | ```c 42 | double num; 43 | 44 | num = uprocd_config_number("Present"); 45 | printf("Present: %d\n", num); // Present: 10 46 | 47 | num = uprocd_config_number("NotPresent"); 48 | printf("NotPresent: %d\n", num); // NotPresent: 0 49 | ``` 50 | 51 | ## SEE ALSO 52 | 53 | uprocd.index(7), uprocd.h(3), uprocd_config_present(3), uprocd_config_number_at(3) 54 | -------------------------------------------------------------------------------- /web/man-extra.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto+Mono'); 2 | 3 | .mp, .mp code, .mp pre, .mp tt, .mp kbd, .mp samp, .mp h3, .mp h4 { 4 | font-family: "Roboto Mono", monospace; 5 | } 6 | 7 | .mp { 8 | max-width: initial; 9 | padding: 1em 2em; 10 | margin-left: 4em; 11 | } 12 | 13 | .mp h2 { 14 | margin-left: -4em; 15 | } 16 | 17 | pre { 18 | overflow-x: auto; 19 | } 20 | 21 | pre::-webkit-scrollbar { 22 | height: 5px; 23 | } 24 | 25 | pre::-webkit-scrollbar-track { 26 | background: #EEEEEE; 27 | } 28 | 29 | pre::-webkit-scrollbar-thumb { 30 | background: #9E9E9E; 31 | } 32 | 33 | pre::-webkit-scrollbar-thumb:hover { 34 | background: #757575; 35 | } 36 | 37 | .mp > p, .mp > pre, .mp > ul, .mp > ol, .mp > dl { 38 | margin-left: 0; 39 | } 40 | 41 | @media only screen and (max-width: 768px) { 42 | .mp { 43 | margin-left: 1em; 44 | } 45 | 46 | .mp h2 { 47 | margin-left: -1em; 48 | } 49 | 50 | #SEE-ALSO + p { 51 | text-align: initial; 52 | } 53 | } 54 | 55 | @media only screen and (max-width: 450px) { 56 | ol.man-decor li.tr { 57 | padding-right: 0.5em; 58 | } 59 | } 60 | 61 | @media only screen and (max-width: 430px) { 62 | ol.man-decor li.tr { 63 | padding-right: 1em; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /man/uprocd_config_string_at.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_string_at -- Return the string at the given index of a list 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT const char * uprocd_config_string_at(const char *list, int index); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the string at the given index of the given list. To retrieve 14 | a string property, use uprocd_config_string(3). 15 | 16 | ## RETURN VALUE 17 | 18 | The string value, or NULL if the list is not present or index is invalid. It is 19 | undefined to call this function on a non-string list value. 20 | 21 | ## EXAMPLE 22 | 23 | Module config: 24 | 25 | ```ini 26 | [NativeModule] 27 | 28 | [Properties] 29 | Present=list string 30 | NotPresent=list string 31 | 32 | [Defaults] 33 | Present=first second third 34 | ``` 35 | 36 | Source code: 37 | 38 | ```c 39 | const char *s; 40 | 41 | s = uprocd_config_string_at("Present", 0); 42 | printf("Present[0]: %s\n", s); // Present[0]: first 43 | 44 | s = uprocd_config_string_at("Present", 20); 45 | printf("Present[20]: %s\n", s); // Present[20]: 0 46 | 47 | s = uprocd_config_string_at("NotPresent", 0); 48 | printf("NotPresent[0]: %s\n", s); // NotPresent[0]: 0 49 | ``` 50 | 51 | ## SEE ALSO 52 | 53 | uprocd.index(7), uprocd.h(3), uprocd_config_string_at(3) 54 | -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #ifndef COMMON_H 6 | #define COMMON_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | void * alloc(size_t sz); 19 | void * ralloc(void *p, size_t sz); 20 | #define newa(ty, n) ((ty*)alloc(sizeof(ty) * (n))) 21 | #define new(ty) newa(ty, 1) 22 | 23 | void get_bus_params(const char *module, sds *pservice, sds *pobject); 24 | 25 | void __setproctitle_init(char **argv); 26 | #define setproctitle_init(argc, argv, ...) __setproctitle_init(argv) 27 | void setproctitle(const char *fmt, ...); 28 | 29 | int readline(FILE *fp, sds *pline); 30 | 31 | typedef struct { 32 | Pvoid_t p; 33 | size_t sz; 34 | } table; 35 | 36 | void table_init(table *tbl); 37 | void table_add(table *tbl, const char *key, void *value); 38 | void * table_get(table *tbl, const char *key); 39 | void * table_swap(table *tbl, const char *key, void *value); 40 | char * table_next(table *tbl, char *prev, void **value); 41 | void table_free(table *tbl); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /man/index.ini: -------------------------------------------------------------------------------- 1 | [Index] 2 | uprocd.index(7)=uprocd.index.7.html 3 | 4 | uprocctl(1)=uprocctl.1.html 5 | u(1)=u.1.html 6 | uprocd(7)=uprocd.7.html 7 | 8 | uprocd.module(5)=uprocd.module.5.html 9 | uprocd.h(3)=uprocd.h.3.html 10 | 11 | uprocd_module_directory(3)=uprocd_module_directory.3.html 12 | uprocd_module_path(3)=uprocd_module_path.3.html 13 | uprocd_module_path_free(3)=uprocd_module_path_free.3.html 14 | 15 | uprocd_config_present(3)=uprocd_config_present.3.html 16 | uprocd_config_list_size(3)=uprocd_config_list_size.3.html 17 | uprocd_config_number(3)=uprocd_config_number.3.html 18 | uprocd_config_number_at(3)=uprocd_config_number_at.3.html 19 | uprocd_config_string(3)=uprocd_config_string.3.html 20 | uprocd_config_string_at(3)=uprocd_config_string_at.3.html 21 | 22 | uprocd_context_get_args(3)=uprocd_context_get_args.3.html 23 | uprocd_context_get_env(3)=uprocd_context_get_env.3.html 24 | uprocd_context_get_cwd(3)=uprocd_context_get_cwd.3.html 25 | uprocd_context_free(3)=uprocd_context_free.3.html 26 | 27 | uprocd_context_enter(3)=uprocd_context_enter.3.html 28 | 29 | uprocd_on_exit(3)=uprocd_on_exit.3.html 30 | uprocd_run(3)=uprocd_run.3.html 31 | 32 | uprocd_module_entry(3)=uprocd_module_entry.3.html 33 | 34 | cgrmvd(7)=cgrmvd.7.html 35 | cgrmvd.policy(5)=cgrmvd.policy.5.html 36 | 37 | systemctl(1)=https://www.freedesktop.org/software/systemd/man/systemctl.html 38 | journalctl(1)=https://www.freedesktop.org/software/systemd/man/journalctl.html 39 | prctl(2)=http://man7.org/linux/man-pages/man2/prctl.2.html 40 | -------------------------------------------------------------------------------- /man/uprocd_config_number_at.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_config_number_at -- Return the number at the given index of a list 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT double uprocd_config_number_at(const char *list, int index); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This function will retrieve the number at the given index of the given list. To retrieve 14 | a number property, use uprocd_config_number(3). 15 | 16 | ## RETURN VALUE 17 | 18 | The number's value, or 0 if the list is not present or index is invalid. It is undefined 19 | to call this function on a non-number list value. 20 | 21 | To distinguist between 0 values and non-present lists or invalid indexes, use 22 | uprocd_config_present(3) and uprocd_config_list_size(3). 23 | 24 | ## EXAMPLE 25 | 26 | Module config: 27 | 28 | ```ini 29 | [NativeModule] 30 | 31 | [Properties] 32 | Present=list number 33 | NotPresent=list number 34 | 35 | [Defaults] 36 | Present=10 20 30 37 | ``` 38 | 39 | Source code: 40 | 41 | ```c 42 | double num; 43 | 44 | num = uprocd_config_number_at("Present", 0); 45 | printf("Present[0]: %d\n", num); // Present[0]: 10 46 | 47 | num = uprocd_config_number_at("Present", 20); 48 | printf("Present[20]: %d\n", num); // Present[20]: 0 49 | 50 | num = uprocd_config_number_at("NotPresent", 0); 51 | printf("NotPresent[0]: %d\n", num); // NotPresent[0]: 0 52 | ``` 53 | 54 | ## SEE ALSO 55 | 56 | uprocd.index(7), uprocd.h(3), uprocd_config_present(3), uprocd_config_list_size(3), 57 | uprocd_config_number(3) 58 | -------------------------------------------------------------------------------- /api/uprocd.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #ifndef UPROCD_H 6 | #define UPROCD_H 7 | 8 | #define UPROCD_EXPORT __attribute__((visibility("default"))) 9 | 10 | typedef int (*uprocd_module_entry_type)(); 11 | UPROCD_EXPORT int uprocd_module_entry(); 12 | 13 | UPROCD_EXPORT const char * uprocd_module_directory(); 14 | UPROCD_EXPORT char * uprocd_module_path(const char *path); 15 | UPROCD_EXPORT void uprocd_module_path_free(char *path); 16 | 17 | typedef void (*uprocd_exit_handler)(void *userdata); 18 | UPROCD_EXPORT void uprocd_on_exit(uprocd_exit_handler func, void *userdata); 19 | 20 | UPROCD_EXPORT int uprocd_config_present(const char *key); 21 | UPROCD_EXPORT int uprocd_config_list_size(const char *key); 22 | UPROCD_EXPORT double uprocd_config_number(const char *key); 23 | UPROCD_EXPORT double uprocd_config_number_at(const char *list, int index); 24 | UPROCD_EXPORT const char * uprocd_config_string(const char *key); 25 | UPROCD_EXPORT const char * uprocd_config_string_at(const char *list, int index); 26 | 27 | typedef struct uprocd_context uprocd_context; 28 | 29 | UPROCD_EXPORT uprocd_context * uprocd_run(); 30 | UPROCD_EXPORT void uprocd_context_enter(uprocd_context *ctx); 31 | UPROCD_EXPORT void uprocd_context_free(uprocd_context *ctx); 32 | 33 | UPROCD_EXPORT void uprocd_context_get_args(uprocd_context *ctx, int *pargc, 34 | char ***pargv); 35 | UPROCD_EXPORT const char * uprocd_context_get_cwd(uprocd_context *ctx); 36 | UPROCD_EXPORT const char ** uprocd_context_get_env(uprocd_context *ctx); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /man/uprocd_module_entry.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_module_entry -- Entry point for uprocd modules 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT int uprocd_module_entry(); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | This is not a uprocd API function. Your native modules should implement this function, 14 | which will be called to initialize and run your modules. 15 | 16 | uprocd_module_entry should perform the following: 17 | 18 | 1. Perform necessary initialization tasks. 19 | 2. Optionally call uprocd_on_exit(3) to set up any failure handlers if an error occurs. 20 | 3. Call uprocd_run(3) to fork the process and retrieve a context. 21 | 4. Use the **uprocd_context_*** functions to retrieve necessary information, such as the 22 | desired environment. 23 | 5. Perform set up steps that should be done before the new context is entered. 24 | 6. Call uprocd_context_enter(3) to enter the new context. 25 | 7. Call uprocd_context_free(3) only if you are done working with the context or any 26 | of its return values. 27 | 8. Run the main module code. 28 | 9. Return the desired exit code. 29 | 30 | If this seems confusing, see uprocd.h(3) and uprocd(7) for a higher-level overview 31 | of the inner workings of uprocd. 32 | 33 | ## EXAMPLE 34 | 35 | ```c 36 | // This is an example module. 37 | 38 | #include 39 | 40 | UPROCD_EXPORT int uprocd_module_entry() { 41 | initialize(); 42 | uprocd_context *ctx = uprocd_run(); 43 | 44 | int argc; 45 | char **argv; 46 | uprocd_context_get_args(&argc, &argv); 47 | set_program_arguments(argc, argv); 48 | 49 | run_main_program(); 50 | uprocd_context_free(ctx); 51 | return 0; 52 | } 53 | ``` 54 | 55 | ## SEE ALSO 56 | 57 | uprocd.index(7), uprocd.h(3), uprocd_on_exit(3), uprocd_run(3), uprocd_context_enter(3), 58 | uprocd_context_free(3), uprocd(7) 59 | -------------------------------------------------------------------------------- /man/uprocd_run.3.md: -------------------------------------------------------------------------------- 1 | # uprocd_run -- Enter the main uprocd daemon and fork the process 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #include 7 | 8 | UPROCD_EXPORT uprocd_context * uprocd_run(); 9 | ``` 10 | 11 | ## DESCRIPTION 12 | 13 | When this function is called from a native module, it will wait for a request, then 14 | return from a new, forked process. It will return a context, which will contain the 15 | necessary information from uprocctl(1) to mimic the original session. Once the module 16 | is ready, they should call uprocd_context_enter(3) to update the current environment 17 | to match that of the context. 18 | 19 | Here is a more detailed description of what happens: 20 | 21 | 1. A native module is loaded, performing steps further detailed in 22 | uprocd_module_entry(3). 23 | 24 | 2. The native module, after initialization, calls **uprocd_run**. 25 | 26 | 3. **uprocd_run** will begin receiving D-Bus messages from uprocctl(3). When a message 27 | to spawn a new program is received, **uprocd_run(3)** will retrieve uprocctl(3)'s 28 | environment, place it in a context, and fork the process. After the fork is complete, 29 | the new context is returned. 30 | 31 | 4. At this point, the module is now running inside of the forked process. It is now 32 | the module's responsibility to initialize needed values, then enter the context via 33 | uprocd_context_enter(3). 34 | 35 | ## RETURN VALUE 36 | 37 | A context object. This function will never properly return from the original process; 38 | it will only ever return from the forked processes. If there is code that should be run 39 | if an error occurs in the original process, use uprocd_on_exit(3) to attach an exit 40 | handler that will be run if an error occurs. 41 | 42 | ## SEE ALSO 43 | 44 | uprocd.index(7), uprocd.h(3), uprocctl(1), uprocd_context_enter(3), 45 | uprocd_module_entry(3), uprocd_on_exit(3) 46 | -------------------------------------------------------------------------------- /man/uprocctl.1.md: -------------------------------------------------------------------------------- 1 | # uprocctl, u -- Query or run uprocd modules 2 | 3 | ## SYNOPSIS 4 | 5 | **uprocctl** [-h] status [MODULE] 6 | 7 | **uprocctl** [-h] run [MODULE] [ARGS...] 8 | 9 | **u** [-h] [MODULE] [ARGS...] 10 | 11 | **u**[MODULE] [ARGS...] 12 | 13 | ## DESCRIPTION 14 | 15 | **uprocctl** allows you to perform high-level communication with uprocd modules. You can 16 | query information about the module, or run a command through it. For module state 17 | management (e.g. starting and stopping a module, or getting logs), use systemctl(1) 18 | and journalctl(1) (see uprocd(7) for a guide). 19 | 20 | **u** is simply an alias for **uprocctl run**. If a symlink to uprocctl is created with 21 | a **u** prefix (such as upython), the prefix will be removed and the filename will be 22 | assumed to be a module. For instance, upython -h is equivalent to u python -h or 23 | uprocctl run python -h. 24 | 25 | **-h** or **--help** can be used to show a help screen. 26 | 27 | ## COMMANDS 28 | 29 | **status** 30 | 31 | > Shows the status of the given uprocd module. 32 | 33 | **run** 34 | 35 | > Forks the given uprocd module, connecting its standard I/O to the current terminal. 36 | > uprocctl will trace the module, exiting with the module's exit code. Any arguments 37 | > given will be passed down to the module. 38 | 39 | uprocctl will rename its process name to that specified by the module, or to the 40 | module's own name if no process name was given. 41 | 42 | ## EXAMPLES 43 | 44 | Check the status of the python module: 45 | 46 | ``` 47 | $ uprocctl status python 48 | ``` 49 | 50 | Run print(123) using the python module: 51 | 52 | ``` 53 | $ uprocctl run python -c 'print 123' 54 | ``` 55 | 56 | Run IPython via the ipython module: 57 | 58 | ``` 59 | $ uprocctl run ipython 60 | ``` 61 | 62 | Equivalent to above: 63 | 64 | ``` 65 | $ u ipython 66 | $ uipython 67 | ``` 68 | 69 | ## SEE ALSO 70 | 71 | uprocd.index(7), uprocd(7), systemctl(1), journalctl(1) 72 | -------------------------------------------------------------------------------- /man/cgrmvd.7.md: -------------------------------------------------------------------------------- 1 | # cgrmvd -- The CGRoup MoVer Daemon 2 | 3 | ## DESCRIPTION 4 | 5 | **cgrmvd** is a daemon that lets you define processes who can be moved to the cgroups of 6 | other processes. In short, you can have a process foo be moved to the exact same 7 | cgroups as process bar, provided a policy file is present (cgrmvd.policy(5)). The 8 | process being moved (foo) is referred to as the *copier*, and the process whose cgroups 9 | are being copied (bar) is referred to as the *origin*. 10 | 11 | The advantage is that unprivileged processes are able to use cgrmvd via D-Bus, and the 12 | only root access required would be to install the proper policy file. 13 | 14 | ## D-BUS 15 | 16 | cgrmvd comes with a simple D-Bus API for moving cgroups. 17 | 18 | **com.refi64.uprocd.Cgrmvd.MoveCgroup('x' copier, 'x' origin)** 19 | 20 | > Move a process to another's cgroups. **copier** specifies the PID of the process that 21 | > should be moved, and **origin** specifies the PID of the process whose cgroups **copier** 22 | > should be moved to. 23 | 24 | > The pathnames for the processes should be specified in a .policy file, otherwise 25 | > the move will be rejected. (This is to ensure random processes don't try to move 26 | > cgroups around.) 27 | 28 | ## POLICIES 29 | 30 | Policy files are stored in /usr/share/cgrmvd/policies. For more information, see 31 | cgrmvd.policy(5). 32 | 33 | ## SECURITY 34 | 35 | Process pathnames are checked using /proc//exe. In theory, this could be a 36 | security risk; since processes can change this symlink via prctl(2) with 37 | PR_SET_MM_EXE_FILE, it could mask itself as another process in order to pass the 38 | policies. 39 | 40 | However, in order to use prctl(2) with PR_SET_MM, the CAP_SYS_RESOURCE capability is 41 | required, and one would need root access in order to give a file this capability. 42 | Therefore, if one is able to give any file the CAP_SYS_RESOURCE capability, they would 43 | already have to be root, and therefore have the ability to move processes between 44 | cgroups anyway. 45 | 46 | ## SEE ALSO 47 | 48 | uprocd.index(7), cgrmvd.policy(5), prctl(2) 49 | -------------------------------------------------------------------------------- /src/uprocd/private.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #ifndef PRIVATE_H 6 | #define PRIVATE_H 7 | 8 | #include "common.h" 9 | 10 | #include 11 | 12 | void _message(int failure, sds error); 13 | 14 | #define _MESSAGE(failure, ...) _message(failure, sdscatfmt(sdsempty(), __VA_ARGS__)) 15 | #define INFO(...) _MESSAGE(0, __VA_ARGS__) 16 | #define FAIL(...) _MESSAGE(1, __VA_ARGS__) 17 | 18 | typedef struct user_type { 19 | enum { TYPE_NONE, TYPE_LIST, TYPE_STRING, TYPE_NUMBER } kind; 20 | struct user_type *child; 21 | } user_type; 22 | 23 | user_type *user_type_clone(user_type *type); 24 | void user_type_free(user_type *type); 25 | 26 | typedef struct user_value { 27 | user_type *type; 28 | union { 29 | struct { 30 | struct user_value **items; 31 | int len; 32 | } list; 33 | sds string; 34 | double number; 35 | }; 36 | } user_value; 37 | 38 | user_value *user_value_parse(sds name, sds value, user_type *type); 39 | void user_value_free(user_value *usr); 40 | 41 | typedef struct config { 42 | enum { CONFIG_NATIVE_MODULE = 1, CONFIG_DERIVED_MODULE } kind; 43 | sds path, process_name, description; 44 | union { 45 | struct { 46 | sds native_lib; 47 | table props, values; 48 | } native; 49 | struct { 50 | sds base; 51 | table value_strings; 52 | } derived; 53 | }; 54 | } config; 55 | 56 | config *config_parse(const char *path); 57 | void config_move_out_values(config *cfg, table *values); 58 | void config_free(config *cfg); 59 | 60 | int prepare_context_and_fork(int argc, char **argv, table *env, char *cwd, int *fds, 61 | pid_t pid); 62 | typedef struct bus_data bus_data; 63 | bus_data * bus_new(); 64 | int bus_pump(bus_data *data); 65 | void bus_free(bus_data *data); 66 | 67 | struct { 68 | char *module; 69 | sds module_dir; 70 | sds process_name, description; 71 | table config; 72 | jmp_buf return_to_main, return_to_loop; 73 | void *exit_handler, *exit_handler_userdata; 74 | void *upcoming_context; 75 | } global_run_data; 76 | 77 | #endif 78 | 79 | -------------------------------------------------------------------------------- /LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | Contents of sds/ 2 | ================ 3 | 4 | Copyright (c) 2006-2014, Salvatore Sanfilippo 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | Conents of scripts/ronn/ 30 | ======================== 31 | 32 | Copyright (C) 2009 Ryan Tomayko 33 | 34 | Permission is hereby granted, free of charge, to any person ob- 35 | taining a copy of this software and associated documentation 36 | files (the "Software"), to deal in the Software without restric- 37 | tion, including without limitation the rights to use, copy, modi- 38 | fy, merge, publish, distribute, sublicense, and/or sell copies of 39 | the Software, and to permit persons to whom the Software is fur- 40 | nished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be 43 | included in all copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 46 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 47 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN- 48 | FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 49 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 50 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 51 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 52 | SOFTWARE. 53 | 54 | -------------------------------------------------------------------------------- /modules/ruby/ruby.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "uprocd.h" 6 | 7 | #ifdef __GNUC__ 8 | #pragma GCC diagnostic push 9 | #pragma GCC diagnostic ignored "-Wunknown-attributes" 10 | #pragma GCC diagnostic ignored "-Wignored-attributes" 11 | #endif 12 | 13 | #include 14 | 15 | #ifdef __GCC__ 16 | #pragma GCC diagnostic pop 17 | #endif 18 | 19 | void set_environment(uprocd_context *ctx) { 20 | VALUE rbenv = rb_const_get_at(rb_cObject, rb_intern("ENV")); 21 | rb_funcall(rbenv, rb_intern("clear"), 0); 22 | 23 | for (const char **env = uprocd_context_get_env(ctx); *env != NULL; env += 2) { 24 | VALUE key = rb_str_new_cstr(env[0]), 25 | value = rb_str_new_cstr(env[1]); 26 | rb_funcall(rbenv, rb_intern("store"), 2, key, value); 27 | } 28 | } 29 | 30 | static void check_error(const char *message) { 31 | VALUE exc = rb_errinfo(); 32 | rb_set_errinfo(Qnil); 33 | 34 | VALUE msg = rb_funcall(exc, rb_intern("full_message"), 0); 35 | rb_funcall(rb_mKernel, rb_intern("puts"), 2, rb_str_new_cstr(message), msg); 36 | } 37 | 38 | VALUE ruby_entry(VALUE udata) { 39 | VALUE verbose = ruby_verbose; 40 | ruby_verbose = Qnil; 41 | 42 | const char *preload = uprocd_config_string("Preload"); 43 | const char *load_options[] = {"ruby", "-I", uprocd_module_directory(), 44 | "-r_uprocd_requires", "-e", preload}; 45 | int state = ruby_exec_node( 46 | ruby_options(sizeof(load_options) / sizeof(load_options[0]), (char**)load_options)); 47 | if (state) { 48 | check_error("Error running preload code:"); 49 | return 0; 50 | } 51 | 52 | uprocd_context *ctx = uprocd_run(); 53 | uprocd_context_enter(ctx); 54 | 55 | set_environment(ctx); 56 | 57 | int argc; 58 | char **argv; 59 | uprocd_context_get_args(ctx, &argc, &argv); 60 | 61 | const char *run = uprocd_config_string("Run"); 62 | int has_run_offset = 0; 63 | if (strlen(run) != 0) { 64 | has_run_offset = 3; 65 | } 66 | 67 | int ruby_argc = argc + has_run_offset; 68 | const char *ruby_argv[ruby_argc]; 69 | 70 | if (has_run_offset) { 71 | ruby_argv[0] = "ruby"; 72 | ruby_argv[1] = "-e"; 73 | ruby_argv[2] = run; 74 | } 75 | 76 | for (int i = 0; i < argc; i++) { 77 | ruby_argv[i + has_run_offset] = argv[i]; 78 | } 79 | 80 | void *node = ruby_options(ruby_argc, (char**)ruby_argv); 81 | 82 | uprocd_context_free(ctx); 83 | 84 | ruby_verbose = verbose; 85 | return ruby_run_node(node); 86 | } 87 | 88 | UPROCD_EXPORT int uprocd_module_entry() { 89 | ruby_init(); 90 | ruby_init_loadpath(); 91 | 92 | int state; 93 | int ret = rb_protect(ruby_entry, 0, &state); 94 | if (state) { 95 | check_error("Error:"); 96 | return 1; 97 | } else { 98 | return ret; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /man/uprocd.7.md: -------------------------------------------------------------------------------- 1 | # uprocd -- Guide on managing uprocd modules with systemd 2 | 3 | ## DESCRIPTION 4 | 5 | **uprocd** is a tool that starts processes in the background, then "freezes" them. When 6 | you run a program using uprocctl(1), uprocd will fork the process and connect it to 7 | the current terminal. This is essentially a form of "process caching" or "process 8 | preloading"; processes will start more quickly, as the expensive initialization steps 9 | have already taken place in the background. 10 | 11 | ## MODULES 12 | 13 | Module files are stored in any of the following locations: 14 | 15 | - /usr/share/uprocd/modules - Built-in modules. 16 | - /usr/local/share/uprocd/modules - Installed modules. 17 | - $XDG_CONFIG_HOME/uprocd/modules - User modules. 18 | 19 | (For more information on the module format, see uprocd.module(5).) 20 | 21 | Each uprocd module is a seperate daemon, and all are started via the systemd unit 22 | uprocd@. Therefore, primary management of uprocd modules uses systemctl(1). In 23 | order to access a module, use the uprocd@ template unit, with the instance name set to 24 | the module name. For instance, the python module will be under the systemd name 25 | uprocd@python. uprocd logs can be viewed through either **systemctl status** or 26 | journalctl(1). 27 | 28 | Querying information about, or running, modules should be done via uprocctl(1) 29 | 30 | ## MODULE MANAGEMENT EXAMPLES 31 | 32 | In order to load a module, use **systemctl --user start**: 33 | 34 | ``` 35 | $ systemctl --user start uprocd@module-name 36 | ``` 37 | 38 | For example, to start the *python* module, run: 39 | 40 | ``` 41 | $ systemctl --user start uprocd@python 42 | ``` 43 | 44 | To check module's status and logs, use **systemctl --user status**: 45 | 46 | ``` 47 | $ systemctl --user status uprocd@module-name 48 | ``` 49 | 50 | Other commands: 51 | 52 | ``` 53 | $ systemctl --user stop uprocd@module-name 54 | $ systemctl --user restart uprocd@module-name 55 | ``` 56 | 57 | To view the full, non-truncated logs, use journalctl(1): 58 | 59 | ``` 60 | $ journalctl -b --user-unit uprocd@module-name 61 | ``` 62 | 63 | ## WORKINGS 64 | 65 | It is recommended you read this section if you plan on creating uprocd modules! 66 | 67 | When you start a uprocd service via systemd, a **uprocd** daemon is run for the module. 68 | Every module has its own daemon. The module will immediately perform any initialization 69 | steps, followed by relinquishing control back to the daemon. When the user requests for 70 | a program to be run via uprocctl(1), the daemon will fork, and the forked process 71 | will give a "context" back to the module. 72 | 73 | The context object contains the environment of and arguments passed to uprocctl(1). 74 | When the native module "enters" the context, its environment will be updated to match 75 | that of the uprocctl(1) caller, and it will be attached to the caller's terminal. 76 | After this, the native module will run the desired program. 77 | 78 | ## SEE ALSO 79 | 80 | uprocd.index(7), uprocctl(1), uprocd.module(5), systemctl(1), journalctl(1) 81 | -------------------------------------------------------------------------------- /man/uprocd.h.3.md: -------------------------------------------------------------------------------- 1 | # uprocd.h -- uprocd native module API index 2 | 3 | ## SYNOPSIS 4 | 5 | ```c 6 | #define UPROCD_EXPORT __attribute__((visibility("default"))) 7 | 8 | typedef void (*uprocd_module_entry_type)(); 9 | UPROCD_EXPORT void uprocd_module_entry(); 10 | 11 | UPROCD_EXPORT const char * uprocd_module_directory(); 12 | UPROCD_EXPORT char * uprocd_module_path(const char *path); 13 | UPROCD_EXPORT void uprocd_module_path_free(char *path); 14 | 15 | typedef void (*uprocd_exit_handler)(void *userdata); 16 | UPROCD_EXPORT void uprocd_on_exit(uprocd_exit_handler func, void *userdata); 17 | 18 | UPROCD_EXPORT int uprocd_config_present(const char *key); 19 | UPROCD_EXPORT int uprocd_config_list_size(const char *key); 20 | UPROCD_EXPORT double uprocd_config_number(const char *key); 21 | UPROCD_EXPORT double uprocd_config_number_at(const char *list, int index); 22 | UPROCD_EXPORT const char * uprocd_config_string(const char *key); 23 | UPROCD_EXPORT const char * uprocd_config_string_at(const char *list, int index); 24 | 25 | typedef struct uprocd_context uprocd_context; 26 | 27 | UPROCD_EXPORT uprocd_context * uprocd_run(); 28 | UPROCD_EXPORT void uprocd_context_enter(uprocd_context *ctx); 29 | UPROCD_EXPORT void uprocd_context_free(uprocd_context *ctx); 30 | 31 | UPROCD_EXPORT void uprocd_context_get_args(uprocd_context *ctx, int *pargc, 32 | char ***pargv); 33 | UPROCD_EXPORT const char * uprocd_context_get_cwd(uprocd_context *ctx); 34 | UPROCD_EXPORT const char ** uprocd_context_get_env(uprocd_context *ctx); 35 | ``` 36 | 37 | ## DESCRIPTION 38 | 39 | This is the header file containing the API to be used by native modules. 40 | 41 | ## GETTING STARTED 42 | 43 | uprocd(7) - Ensure you've already read how uprocd works 44 | 45 | ## MODULE BASICS 46 | 47 | uprocd_module_entry(3) - Entry point for uprocd modules 48 | 49 | uprocd_module_directory(3) - Retrieve the path to the current uprocd module 50 | 51 | uprocd_module_path(3) - Retrieve the path to a file next to the current uprocd module 52 | 53 | uprocd_module_path_free(3) - Free a uprocd_module_path(3) return value 54 | 55 | uprocd_on_exit(3) - Set a handler to be called on uprocd_run(3) failure 56 | 57 | ## ACCESSING MODULE PROPERTIES 58 | 59 | uprocd_config_present(3) - Determine if the given property is present 60 | 61 | uprocd_config_list_size(3) - Return the size of the list at the given property 62 | 63 | uprocd_config_number(3) - Return the number at the given property 64 | 65 | uprocd_config_number_at(3) - Return the number at the given index of a list 66 | 67 | uprocd_config_string(3) - Return the string at the given property 68 | 69 | uprocd_config_string_at(3) - Return the string at the given index of a list 70 | 71 | ## USING CONTEXTS 72 | 73 | uprocd_run(3) - Enter the main uprocd daemon and fork the process 74 | 75 | uprocd_context_enter(3) - Enter a uprocd context object 76 | 77 | uprocd_context_free(3) - Free a uprocd context 78 | 79 | uprocd_context_get_args(3) - Retrieve arguments from a uprocd context 80 | 81 | uprocd_context_get_cwd(3) - Retrieve the working directory from a uprocd context 82 | 83 | uprocd_context_get_env(3) - Retrieve the environment from a uprocd context 84 | -------------------------------------------------------------------------------- /modules/python/python.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "uprocd.h" 6 | 7 | #include 8 | 9 | void set_environment(uprocd_context *ctx) { 10 | PyObject *osmod = NULL, *environ = NULL; 11 | 12 | osmod = PyImport_ImportModule("os"); 13 | if (osmod == NULL) { 14 | PyErr_Print(); 15 | goto end; 16 | } 17 | 18 | environ = PyObject_GetAttrString(osmod, "environ"); 19 | if (environ == NULL) { 20 | PyErr_Print(); 21 | goto end; 22 | } 23 | 24 | PyObject *clear_result = PyObject_CallMethod(environ, "clear", NULL); 25 | if (clear_result == NULL) { 26 | PyErr_Print(); 27 | goto end; 28 | } 29 | Py_DECREF(clear_result); 30 | 31 | for (const char **env = uprocd_context_get_env(ctx); *env != NULL; env += 2) { 32 | PyObject *key = PyUnicode_FromString(env[0]), 33 | *value = PyUnicode_FromString(env[1]); 34 | int rc = PyObject_SetItem(environ, key, value); 35 | Py_DECREF(key); 36 | Py_DECREF(value); 37 | if (rc == -1) { 38 | PyErr_Print(); 39 | } 40 | } 41 | 42 | end: 43 | if (environ) { 44 | Py_DECREF(environ); 45 | } 46 | if (osmod) { 47 | Py_DECREF(osmod); 48 | } 49 | } 50 | 51 | UPROCD_EXPORT int uprocd_module_entry() { 52 | Py_SetProgramName(L"python"); 53 | Py_Initialize(); 54 | 55 | char *modules_path = uprocd_module_path("_uprocd_modules.py"); 56 | FILE *modules = fopen(modules_path, "r"); 57 | if (modules == NULL) { 58 | fprintf(stderr, "Error loading _uprocd_modules.py: %s\n", strerror(errno)); 59 | return 1; 60 | } 61 | 62 | PyRun_SimpleFileEx(modules, modules_path, 1); 63 | uprocd_module_path_free(modules_path); 64 | 65 | const char *preload = uprocd_config_string("Preload"); 66 | PyRun_SimpleString(preload); 67 | 68 | uprocd_context *ctx = uprocd_run(); 69 | uprocd_context_enter(ctx); 70 | 71 | set_environment(ctx); 72 | 73 | int argc, has_run_offset = 0; 74 | char **argv; 75 | uprocd_context_get_args(ctx, &argc, &argv); 76 | 77 | const char *run = uprocd_config_string("Run"); 78 | if (strlen(run) != 0) { 79 | has_run_offset = 2; 80 | } 81 | 82 | argc += has_run_offset; 83 | 84 | wchar_t **wargv = PyMem_RawMalloc(sizeof(wchar_t*) * (argc + 1)); 85 | if (wargv == NULL) { 86 | fprintf(stderr, "Error initializing Python interpreter: Out of memory.\n"); 87 | return 1; 88 | } 89 | 90 | wargv[0] = L"python"; 91 | 92 | if (has_run_offset) { 93 | wargv[1] = L"-c"; 94 | wargv[2] = Py_DecodeLocale(run, NULL); 95 | if (wargv[2] == NULL) { 96 | fprintf(stderr, "Error decoding Run argument.\n"); 97 | return 1; 98 | } 99 | } 100 | 101 | for (int i = has_run_offset + 1; i < argc; i++) { 102 | wargv[i] = Py_DecodeLocale(argv[i - has_run_offset], NULL); 103 | if (wargv[i] == NULL) { 104 | fprintf(stderr, "Error initializing Python interpreter: Error decoding argv.\n"); 105 | return 1; 106 | } 107 | } 108 | 109 | wargv[argc] = NULL; 110 | int ret = Py_Main(argc, wargv); 111 | 112 | uprocd_context_free(ctx); 113 | return ret; 114 | } 115 | -------------------------------------------------------------------------------- /src/uprocd/config.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | #include "private.h" 7 | 8 | user_type *user_type_clone(user_type *type) { 9 | user_type *res = new(user_type); 10 | res->kind = type->kind; 11 | if (type->kind == TYPE_LIST) { 12 | res->child = user_type_clone(type->child); 13 | } 14 | return res; 15 | } 16 | 17 | void user_type_free(user_type *type) { 18 | if (type->kind == TYPE_LIST && type->child) { 19 | user_type_free(type->child); 20 | } 21 | free(type); 22 | } 23 | 24 | user_value *user_value_parse(sds name, sds value, user_type *type) { 25 | user_value *res = new(user_value); 26 | char *ep; 27 | sds *parts; 28 | 29 | switch (type->kind) { 30 | case TYPE_NONE: abort(); 31 | case TYPE_NUMBER: 32 | res->number = strtod(value, &ep); 33 | if (*ep) { 34 | FAIL("Error parsing value of %S: Invalid number.", name); 35 | free(res); 36 | return NULL; 37 | } 38 | break; 39 | case TYPE_STRING: 40 | res->string = sdsdup(value); 41 | break; 42 | case TYPE_LIST: 43 | parts = sdssplitlen(value, sdslen(value), " ", 1, &res->list.len); 44 | res->list.items = newa(user_value*, res->list.len); 45 | for (int i = 0; i < res->list.len; i++) { 46 | user_value *child = user_value_parse(name, parts[i], type->child); 47 | if (child == NULL) { 48 | user_value_free(res); 49 | return NULL; 50 | } 51 | res->list.items[i] = child; 52 | } 53 | sdsfreesplitres(parts, res->list.len); 54 | break; 55 | } 56 | 57 | res->type = user_type_clone(type); 58 | return res; 59 | } 60 | 61 | void user_value_free(user_value *usr) { 62 | if (usr->type) { 63 | if (usr->type->kind == TYPE_STRING) { 64 | sdsfree(usr->string); 65 | } else if (usr->type->kind == TYPE_LIST) { 66 | for (int i = 0; i < usr->list.len; i++) { 67 | if (usr->list.items[i]) { 68 | user_value_free(usr->list.items[i]); 69 | } 70 | } 71 | } 72 | } 73 | 74 | free(usr); 75 | } 76 | 77 | void config_move_out_values(config *cfg, table *values) { 78 | *values = cfg->native.values; 79 | table_init(&cfg->native.values); 80 | } 81 | 82 | void config_free(config *cfg) { 83 | if (cfg->path) { 84 | sdsfree(cfg->path); 85 | } 86 | if (cfg->process_name) { 87 | sdsfree(cfg->process_name); 88 | } 89 | if (cfg->description) { 90 | sdsfree(cfg->description); 91 | } 92 | 93 | char *arg = NULL; 94 | 95 | switch (cfg->kind) { 96 | case CONFIG_NATIVE_MODULE: 97 | if (cfg->native.native_lib) { 98 | sdsfree(cfg->native.native_lib); 99 | } 100 | 101 | user_type *type; 102 | while ((arg = table_next(&cfg->native.props, arg, (void**)&type))) { 103 | user_type_free(type); 104 | } 105 | table_free(&cfg->native.props); 106 | 107 | user_value *usr; 108 | arg = NULL; 109 | while ((arg = table_next(&cfg->native.values, arg, (void**)&usr))) { 110 | user_value_free(usr); 111 | } 112 | table_free(&cfg->native.values); 113 | break; 114 | case CONFIG_DERIVED_MODULE: 115 | if (cfg->derived.base) { 116 | sdsfree(cfg->derived.base); 117 | } 118 | 119 | sds value; 120 | while ((arg = table_next(&cfg->derived.value_strings, arg, (void**)&value))) { 121 | sdsfree(value); 122 | } 123 | table_free(&cfg->derived.value_strings); 124 | break; 125 | } 126 | 127 | free(cfg); 128 | } 129 | -------------------------------------------------------------------------------- /src/common/common.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | 7 | #include 8 | 9 | void * alloc(size_t sz) { 10 | void *res = calloc(sz, 1); 11 | if (res == NULL) { 12 | fputs("Out of memory.", stderr); 13 | abort(); 14 | } 15 | return res; 16 | } 17 | 18 | void * ralloc(void *p, size_t sz) { 19 | void *res = realloc(p, sz); 20 | if (res == NULL) { 21 | fputs("Out of memory.", stderr); 22 | free(p); 23 | abort(); 24 | } 25 | return res; 26 | } 27 | 28 | void get_bus_params(const char *module, sds *pservice, sds *pobject) { 29 | *pservice = sdscat(sdsnew("com.refi64.uprocd.modules."), module); 30 | *pobject = sdscat(sdsnew("/com/refi64/uprocd/modules/"), module); 31 | } 32 | 33 | static char **g_argv; 34 | 35 | void __setproctitle_init(char **argv) { 36 | g_argv = argv; 37 | } 38 | 39 | void setproctitle(const char *fmt, ...) { 40 | sds name; 41 | if (fmt[0] == '-') { 42 | fmt++; 43 | name = sdsempty(); 44 | } else { 45 | name = sdscat(sdsnew(g_argv[0]), " "); 46 | } 47 | 48 | va_list args; 49 | va_start(args, fmt); 50 | name = sdscatvprintf(name, fmt, args); 51 | va_end(args); 52 | 53 | prctl(PR_SET_NAME, name, NULL, NULL, NULL); 54 | sdsfree(name); 55 | } 56 | 57 | int readline(FILE *fp, sds *pline) { 58 | sds result = sdsempty(); 59 | *pline = NULL; 60 | char buf[128]; 61 | buf[0] = 0; 62 | for (;;) { 63 | if (fgets(buf, sizeof(buf), fp) == NULL) { 64 | if (ferror(fp)) { 65 | int errno_ = errno; 66 | sdsfree(result); 67 | return -errno_; 68 | } else if (feof(fp)) { 69 | break; 70 | } 71 | } 72 | 73 | result = sdscat(result, buf); 74 | if (result[sdslen(result) - 1] == '\n') { 75 | break; 76 | } 77 | } 78 | if (sdslen(result) == 0) { 79 | sdsfree(result); 80 | return 0; 81 | } 82 | sdstrim(result, "\n"); 83 | *pline = result; 84 | return 0; 85 | } 86 | 87 | void table_init(table *tbl) { 88 | tbl->p = NULL; 89 | tbl->sz = 0; 90 | } 91 | 92 | void table_add(table *tbl, const char *key, void *value) { 93 | Word_t *pvalue; 94 | JSLI(pvalue, tbl->p, (const uint8_t*)key); 95 | *pvalue = (Word_t)value; 96 | tbl->sz++; 97 | } 98 | 99 | void * table_get(table *tbl, const char *key) { 100 | Word_t *pvalue; 101 | JSLG(pvalue, tbl->p, (const uint8_t*)key); 102 | if (pvalue) { 103 | return *(void**)pvalue; 104 | } else { 105 | return NULL; 106 | } 107 | } 108 | 109 | void * table_swap(table *tbl, const char *key, void *value) { 110 | void *orig = table_get(tbl, key); 111 | table_add(tbl, key, value); 112 | return orig; 113 | } 114 | 115 | int table_del(table *tbl, const char *key) { 116 | int rc; 117 | JSLD(rc, tbl->p, (const uint8_t*)key); 118 | if (rc) { 119 | tbl->sz--; 120 | } 121 | return rc; 122 | } 123 | 124 | char * table_next(table *tbl, char *prev, void **value) { 125 | Word_t *pvalue; 126 | uint8_t idx[4096]; 127 | if (prev == NULL) { 128 | idx[0] = 0; 129 | JSLF(pvalue, tbl->p, idx); 130 | } else { 131 | strncpy((char*)idx, prev, sizeof(idx)); 132 | JSLN(pvalue, tbl->p, idx); 133 | } 134 | free(prev); 135 | if (!pvalue) { 136 | return NULL; 137 | } 138 | size_t len = strlen((char*)idx); 139 | char *res = alloc(len + 1); 140 | memcpy(res, idx, len + 1); 141 | if (value) { 142 | *value = (void*)*pvalue; 143 | } 144 | return res; 145 | } 146 | 147 | void table_free(table *tbl) { 148 | JudySLFreeArray((PPvoid_t)&tbl->p, PJE0); 149 | } 150 | -------------------------------------------------------------------------------- /man/uprocd.module.5.md: -------------------------------------------------------------------------------- 1 | # uprocd.module -- uprocd module configuration format 2 | 3 | ## SYNOPSIS 4 | 5 | module.module 6 | 7 | ## DESCRIPTION 8 | 9 | A module file with the extension .module contains the specification for a uprocd module. 10 | This page lists the format of a uprocd module, as well as several examples. For guidance 11 | on creating a native module library, see uprocd.h(3). 12 | 13 | ## BASIC SYNTAX 14 | 15 | uprocd modules use an INI-inspired syntax, with some tweaks to make module authoring 16 | easier. 17 | 18 | Sections are still declared using [SectionName], and properties within a section using 19 | PropertyName=Value. If a line begins with a #, it will be considered a comment. 20 | 21 | If the line after a Key= line is indented, then the indented line will be considered 22 | a multi-line key value. 23 | 24 | ``` 25 | # My uprocd module. 26 | [Section] 27 | Key=Value 28 | Other= 29 | This is part of the Other key. 30 | Same here. 31 | 32 | This will continue until a line isn't indented. 33 | NormalKey=Value 34 | ``` 35 | 36 | ## MODULE TYPES 37 | 38 | There are two types of modules: native modules and derived modules. 39 | 40 | Native modules consist of a .module file and shared object library (ending in .so) that 41 | contains a symbol uprocd_module_entry(3) that runs the module code. These modules can 42 | specify properties that can be passed to them from derived modules. 43 | 44 | Derived modules are extensions of native modules. These consist of only a .module file, 45 | and the file will give values to the properties that the native module requires. 46 | 47 | The module type is declared using the [NativeModule] or [DerivedModule] config section, 48 | which must be the very first section in the file. 49 | 50 | ## MODULE DECLARATIONS 51 | 52 | [NativeModule] or [DerivedModule] sections may specify the following properties: 53 | 54 | **ProcessName=** 55 | 56 | > The process name. uprocctl will rename itself to this name when running a module. 57 | 58 | **Description=** 59 | 60 | > A description for the module. 61 | 62 | [NativeModule] sections may specify the following properties: 63 | 64 | **NativeLib=** 65 | 66 | > By default, uprocd will look for a .so file with the same name as the .module file. 67 | > This will change the name of the .so file to look for. 68 | 69 | A [DerivedModule] section **must** specify the following properties: 70 | 71 | **Base=** 72 | 73 | > The name of the native module that this derived module is extending. 74 | 75 | ## MODULE PROPERTIES 76 | 77 | Native modules may ask for properties that a derived module must pass. These are 78 | declared via the [Properties] section. Each key in the section will be a property name, 79 | and each value will be the type of property. The following types are supported: 80 | 81 | **string** 82 | 83 | > A string value. 84 | 85 | **number** 86 | 87 | > A number value. This may be either a floating point number or an integer. 88 | 89 | **list string** 90 | 91 | > A space-seperated list of strings. 92 | 93 | **list number** 94 | 95 | > A space-seperated list of numbers. 96 | 97 | In additions, the module should declare default values for all properties that a 98 | derived module does not have to specify. This is done in the [Defaults] section. Each 99 | key is a property name, and each value is a default value for that property. 100 | 101 | In a derived module, the property values will be assigned in the [DerivedModule] 102 | section. 103 | 104 | ## EXAMPLE MODULES 105 | 106 | A native module: 107 | 108 | ``` 109 | [NativeModule] 110 | Description=My native module. 111 | # ProcessName will default to the module's name. 112 | ProcessName=custom-process-name 113 | 114 | [Properties] 115 | LuckyNumbers=list number 116 | LuckyNames=list string 117 | 118 | [Defaults] 119 | LuckyNumbers=1 2 3 4 5 120 | # Because LuckyNames is not given a default, derived modules will be FORCED to specify it. 121 | ``` 122 | 123 | A derived module: 124 | 125 | ``` 126 | [DerivedModule] 127 | Description=My derived module. 128 | Base=native-module 129 | ProcessName=custom-process-name 130 | 131 | LuckyNames=Noctis Luna 132 | ``` 133 | 134 | ## SEE ALSO 135 | 136 | uprocd.index(7), uprocd.h(3), uprocd(7) 137 | -------------------------------------------------------------------------------- /src/uprocd/bus.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "private.h" 6 | 7 | #include 8 | 9 | int service_method_status(sd_bus_message *msg, void *data, sd_bus_error *err) { 10 | char *name = global_run_data.module; 11 | char *description = global_run_data.description ? global_run_data.description : 12 | ""; 13 | 14 | return sd_bus_reply_method_return(msg, "ss", name, description); 15 | } 16 | 17 | int service_method_run(sd_bus_message *msg, void *data, sd_bus_error *err) { 18 | char *title = global_run_data.process_name ? global_run_data.process_name : 19 | global_run_data.module; 20 | 21 | int rc; 22 | table env; 23 | table_init(&env); 24 | 25 | rc = sd_bus_message_enter_container(msg, 'a', "{ss}"); 26 | if (rc < 0) { 27 | goto read_end; 28 | } 29 | 30 | while ((rc = sd_bus_message_enter_container(msg, 'e', "ss")) > 0) { 31 | char *name, *value; 32 | rc = sd_bus_message_read(msg, "ss", &name, &value); 33 | if (rc < 0) { 34 | goto read_end; 35 | } 36 | table_add(&env, name, value); 37 | 38 | rc = sd_bus_message_exit_container(msg); 39 | if (rc < 0) { 40 | goto read_end; 41 | } 42 | } 43 | 44 | rc = sd_bus_message_exit_container(msg); 45 | if (rc < 0) { 46 | goto read_end; 47 | } 48 | 49 | rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "s"); 50 | if (rc < 0) { 51 | goto read_end; 52 | } 53 | 54 | int argc = 1; 55 | sds *argv = new(sds); 56 | argv[0] = sdsnew(title); 57 | 58 | char *arg; 59 | while ((rc = sd_bus_message_read(msg, "s", &arg)) > 0) { 60 | argc++; 61 | argv = ralloc(argv, argc * sizeof(sds)); 62 | argv[argc - 1] = sdsnew(arg); 63 | } 64 | 65 | rc = sd_bus_message_exit_container(msg); 66 | if (rc < 0) { 67 | goto read_end; 68 | } 69 | 70 | char *cwd; 71 | int fds[3]; 72 | int64_t pid; 73 | rc = sd_bus_message_read(msg, "s(hhh)x", &cwd, &fds[0], &fds[1], &fds[2], &pid); 74 | if (rc < 0) { 75 | goto read_end; 76 | } 77 | 78 | read_end: 79 | if (rc < 0) { 80 | FAIL("Error parsing bus message: %s", strerror(-rc)); 81 | return rc; 82 | } 83 | 84 | int child = prepare_context_and_fork(argc, argv, &env, cwd, fds, pid); 85 | if (child < 0) { 86 | sd_bus_message_unref(msg); 87 | return child; 88 | } 89 | 90 | if (child == 0) { 91 | sd_bus_message_unref(msg); 92 | longjmp(global_run_data.return_to_loop, 1); 93 | } else { 94 | return sd_bus_reply_method_return(msg, "xs", child, title); 95 | } 96 | } 97 | 98 | static const sd_bus_vtable service_vtable[] = { 99 | SD_BUS_VTABLE_START(0), 100 | // Status() -> String name, String description 101 | SD_BUS_METHOD("Status", "", "ss", service_method_status, 102 | SD_BUS_VTABLE_UNPRIVILEGED), 103 | // Run(Array> env, Array argv, String cwd, 104 | // Tuple ttys, Int64 uprocctl_pid) -> Int64 pid, String name 105 | SD_BUS_METHOD("Run", "a{ss}ass(hhh)x", "xs", 106 | service_method_run, SD_BUS_VTABLE_UNPRIVILEGED), 107 | SD_BUS_VTABLE_END 108 | }; 109 | 110 | struct bus_data { 111 | sd_bus *bus; 112 | sd_bus_slot *slot; 113 | sds service, object; 114 | }; 115 | 116 | bus_data * bus_new() { 117 | int rc; 118 | bus_data *data = new(bus_data); 119 | 120 | rc = sd_bus_open_user(&data->bus); 121 | if (rc < 0) { 122 | FAIL("sd_bus_open_user failed: %s", strerror(-rc)); 123 | goto failure; 124 | } 125 | 126 | get_bus_params(global_run_data.module, &data->service, &data->object); 127 | rc = sd_bus_add_object_vtable(data->bus, &data->slot, data->object, data->service, 128 | service_vtable, NULL); 129 | if (rc < 0) { 130 | FAIL("sd_bus_add_object_vtable failed: %s", strerror(-rc)); 131 | goto failure; 132 | } 133 | 134 | rc = sd_bus_request_name(data->bus, data->service, 0); 135 | if (rc < 0) { 136 | FAIL("sd_bus_request_name failed: %s", strerror(-rc)); 137 | goto failure; 138 | } 139 | 140 | return data; 141 | 142 | failure: 143 | bus_free(data); 144 | return NULL; 145 | } 146 | 147 | int bus_pump(bus_data *data) { 148 | int rc; 149 | rc = sd_bus_process(data->bus, NULL); 150 | if (rc < 0) { 151 | FAIL("sd_bus_process failed: %s", strerror(-rc)); 152 | return -1; 153 | } else if (rc > 0) { 154 | return 1; 155 | } 156 | 157 | rc = sd_bus_wait(data->bus, (uint64_t)-1); 158 | if (rc < 0 && rc != -EINTR) { 159 | FAIL("sd_bus_wait failed: %s", strerror(-rc)); 160 | return -1; 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | void bus_free(bus_data *data) { 167 | if (data == NULL) { 168 | return; 169 | } 170 | 171 | sd_bus_slot_unref(data->slot); 172 | sd_bus_unref(data->bus); 173 | if (data->service) { 174 | sdsfree(data->service); 175 | } 176 | if (data->object) { 177 | sdsfree(data->object); 178 | } 179 | 180 | free(data); 181 | } 182 | -------------------------------------------------------------------------------- /src/uprocd/config-parse.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | #include "private.h" 7 | 8 | #include 9 | 10 | config *config_parse(const char *path) { 11 | FILE *fp = fopen(path, "r"); 12 | if (fp == NULL) { 13 | FAIL("Error opening config file: %s", strerror(errno)); 14 | return NULL; 15 | } 16 | 17 | config *cfg = new(config); 18 | sds line = NULL, cursect = NULL, key = NULL, value = NULL; 19 | 20 | int lineno = 0, rc = 0; 21 | 22 | #define PARSE_ERROR(fmt, ...) do { \ 23 | FAIL("Error parsing %s:%i: " fmt, path, lineno, ##__VA_ARGS__); \ 24 | rc = 1; \ 25 | } while (0) 26 | 27 | while ((rc = readline(fp, &line)) == 0 && line != NULL) { 28 | lineno++; 29 | sdstrim(line, "\t "); 30 | 31 | size_t len = sdslen(line); 32 | if (len == 0 || line[0] == '#') { 33 | goto parse_end; 34 | } 35 | 36 | if (line[0] == '[') { 37 | if (line[len - 1] != ']') { 38 | PARSE_ERROR("Expected right bracket to end line"); 39 | goto parse_end; 40 | } 41 | 42 | sdsrange(line, 1, -2); 43 | cursect = sdsdup(line); 44 | 45 | int isnative = strcmp(cursect, "NativeModule") == 0, 46 | isderived = strcmp(cursect, "DerivedModule") == 0; 47 | if (isnative || isderived) { 48 | if (cfg->kind) { 49 | PARSE_ERROR("Duplicate module declaration"); 50 | goto parse_end; 51 | } 52 | 53 | cfg->kind = isnative ? CONFIG_NATIVE_MODULE : CONFIG_DERIVED_MODULE; 54 | goto parse_end; 55 | } else if (strcmp(cursect, "Properties") == 0 || 56 | strcmp(cursect, "Defaults") == 0) { 57 | if (cfg->kind != CONFIG_NATIVE_MODULE) { 58 | PARSE_ERROR("%S can only be used with a NativeModule.", cursect); 59 | goto parse_end; 60 | } 61 | } else { 62 | PARSE_ERROR("Invalid section '%S'", cursect); 63 | goto parse_end; 64 | } 65 | } else { 66 | char *eq = memchr(line, '=', len); 67 | if (eq == NULL) { 68 | PARSE_ERROR("Invalid line"); 69 | goto parse_end; 70 | } 71 | 72 | key = sdsdup(line), value = sdsdup(line); 73 | sdsrange(key, 0, eq - line - 1); 74 | sdsrange(value, eq - line + 1, -1); 75 | 76 | int indent = -1; 77 | for (;;) { 78 | char peek = fgetc(fp); 79 | if (peek == '\n') { 80 | continue; 81 | } 82 | 83 | ungetc(peek, fp); 84 | if (peek != ' ') { 85 | break; 86 | } 87 | 88 | sds nextline; 89 | rc = readline(fp, &nextline); 90 | if (rc != 0) { 91 | break; 92 | } 93 | 94 | int current_indent = 0; 95 | while (isspace(nextline[current_indent])) { 96 | current_indent++; 97 | } 98 | if (indent == -1) { 99 | indent = current_indent; 100 | } 101 | 102 | int min_indent = current_indent < indent ? current_indent : indent; 103 | sdsrange(nextline, min_indent, -1); 104 | value = sdscatfmt(value, "\n%S", nextline); 105 | } 106 | 107 | if (!cfg->kind) { 108 | PARSE_ERROR("Key '%S' outside section", key); 109 | goto parse_end; 110 | } 111 | 112 | if (strcmp(cursect, "NativeModule") == 0 || 113 | strcmp(cursect, "DerivedModule") == 0) { 114 | if (strcmp(key, "ProcessName") == 0) { 115 | cfg->process_name = sdsdup(value); 116 | goto parse_end; 117 | } else if (strcmp(key, "Description") == 0) { 118 | cfg->description = sdsdup(value); 119 | goto parse_end; 120 | } 121 | 122 | switch (cfg->kind) { 123 | case CONFIG_NATIVE_MODULE: 124 | if (strcmp(key, "NativeLib") == 0) { 125 | cfg->native.native_lib = sdsdup(value); 126 | goto parse_end; 127 | } 128 | break; 129 | case CONFIG_DERIVED_MODULE: 130 | if (strcmp(key, "Base") == 0) { 131 | cfg->derived.base = sdsdup(value); 132 | goto parse_end; 133 | } else { 134 | table_add(&cfg->derived.value_strings, key, sdsdup(value)); 135 | goto parse_end; 136 | } 137 | } 138 | } else if (strcmp(cursect, "Properties") == 0) { 139 | user_type *type = new(user_type), *current = type; 140 | int islist = 0; 141 | 142 | while (strncmp(value, "list ", 5) == 0) { 143 | if (islist) { 144 | PARSE_ERROR("Nested lists are not allowed"); 145 | user_type_free(type); 146 | goto parse_end; 147 | } 148 | islist = 1; 149 | sdsrange(value, 5, -1); 150 | 151 | type->kind = TYPE_LIST; 152 | type->child = new(user_type); 153 | current = type->child; 154 | } 155 | 156 | if (strcmp(value, "string") == 0) { 157 | current->kind = TYPE_STRING; 158 | } else if (strcmp(value, "number") == 0) { 159 | current->kind = TYPE_NUMBER; 160 | } else { 161 | PARSE_ERROR("Invalid type: '%S'", value); 162 | user_type_free(type); 163 | goto parse_end; 164 | } 165 | 166 | table_add(&cfg->native.props, key, type); 167 | goto parse_end; 168 | } else if (strcmp(cursect, "Defaults") == 0) { 169 | user_type *type = table_get(&cfg->native.props, key); 170 | if (type == NULL) { 171 | PARSE_ERROR("Unknown key: '%S'", key); 172 | goto parse_end; 173 | } 174 | 175 | user_value *usr = user_value_parse(key, value, type); 176 | if (usr == NULL) { 177 | rc = 1; 178 | goto parse_end; 179 | } 180 | 181 | table_add(&cfg->native.values, key, usr); 182 | goto parse_end; 183 | } 184 | 185 | PARSE_ERROR("Invalid key '%S'", key); 186 | goto parse_end; 187 | } 188 | 189 | parse_end: 190 | sdsfree(line); 191 | line = NULL; 192 | if (key) { 193 | sdsfree(key); 194 | key = NULL; 195 | } 196 | if (value) { 197 | sdsfree(value); 198 | value = NULL; 199 | } 200 | if (rc != 0) { 201 | break; 202 | } 203 | } 204 | 205 | fclose(fp); 206 | if (line) { 207 | sdsfree(line); 208 | } 209 | if (cursect) { 210 | sdsfree(cursect); 211 | } 212 | 213 | if (cfg->kind == CONFIG_DERIVED_MODULE && !cfg->derived.base) { 214 | PARSE_ERROR("DerivedModule needs a Base."); 215 | } 216 | 217 | if (rc != 0) { 218 | if (rc < 0) { 219 | FAIL("Error reading %s: %s", path, strerror(-rc)); 220 | } 221 | config_free(cfg); 222 | return NULL; 223 | } 224 | 225 | cfg->path = sdsnew(path); 226 | return cfg; 227 | } 228 | -------------------------------------------------------------------------------- /src/uprocd/main.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | #include "private.h" 7 | 8 | #include "uprocd.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void _message(int failure, sds error) { 18 | fprintf(stderr, "%s%.*s\n", failure ? SD_CRIT : SD_INFO, (int)sdslen(error), error); 19 | if (failure) { 20 | sd_notifyf(0, "STATUS=\"Failure: %s\"", error); 21 | } 22 | sdsfree(error); 23 | } 24 | 25 | sds get_xdg_config_home() { 26 | char *xdg_config_home = getenv("XDG_CONFIG_HOME"); 27 | if (xdg_config_home != NULL) { 28 | return sdsnew(xdg_config_home); 29 | } 30 | 31 | char *home = getenv("HOME"); 32 | return sdscat(sdsnew(home), "/.config"); 33 | } 34 | 35 | config *load_config(const char *module, sds *module_dir) { 36 | sds xdg_config_home = get_xdg_config_home(); 37 | 38 | static const char *search_paths[] = { 39 | /* "build/modules", */ 40 | "/usr/share/uprocd/modules", 41 | "/usr/local/share/uprocd/modules", 42 | "@/uprocd/modules", 43 | }; 44 | 45 | sds module_path = NULL; 46 | 47 | for (int i = 0; i < sizeof(search_paths) / sizeof(search_paths[0]); i++) { 48 | sds path = sdsnew(search_paths[i]); 49 | if (path[0] == '@') { 50 | sdsrange(path, 1, -1); 51 | sds newpath = sdscat(sdsdup(xdg_config_home), path); 52 | sdsfree(path); 53 | path = newpath; 54 | } 55 | 56 | sds test_path = sdscatfmt(sdsdup(path), "/%s.module", module); 57 | INFO("Searching %S...", test_path); 58 | if (access(test_path, F_OK) != -1) { 59 | module_path = test_path; 60 | sdsfree(path); 61 | break; 62 | } 63 | 64 | sdsfree(test_path); 65 | test_path = sdscatfmt(sdsdup(path), "/%s/%s.module", module, module); 66 | INFO("Searching %S...", test_path); 67 | if (access(test_path, F_OK) != -1) { 68 | module_path = test_path; 69 | sdsfree(path); 70 | break; 71 | } 72 | 73 | sdsfree(path); 74 | sdsfree(test_path); 75 | } 76 | 77 | sdsfree(xdg_config_home); 78 | if (module_path == NULL) { 79 | FAIL("Cannot locate module %s config file", module); 80 | return NULL; 81 | } 82 | 83 | if (module_dir) { 84 | *module_dir = sdsdup(module_path); 85 | sdsrange(*module_dir, 0, strrchr(*module_dir, '/') - *module_dir - 1); 86 | } 87 | 88 | INFO("Found module at %S.", module_path); 89 | config *cfg = config_parse(module_path); 90 | sdsfree(module_path); 91 | 92 | if (cfg == NULL) { 93 | return NULL; 94 | } 95 | 96 | return cfg; 97 | } 98 | 99 | config *resolve_derived_config(config *cfg, sds *module_dir) { 100 | INFO("Resolving derived config..."); 101 | 102 | INFO("Locating parent %S.", cfg->derived.base); 103 | config *base = load_config(cfg->derived.base, module_dir); 104 | if (base == NULL) { 105 | config_free(cfg); 106 | return NULL; 107 | } 108 | if (base->kind != CONFIG_NATIVE_MODULE) { 109 | FAIL("A DerivedModule can only have a NativeModule as its base."); 110 | config_free(cfg); 111 | return NULL; 112 | } 113 | 114 | char *key = NULL; 115 | user_type *type; 116 | while ((key = table_next(&base->native.props, key, (void**)&type))) { 117 | sds value = table_get(&cfg->derived.value_strings, key); 118 | if (value == NULL) { 119 | if (table_get(&base->native.values, key) == NULL) { 120 | FAIL("Base module %S requires a value for %s.", cfg->derived.base, key); 121 | config_free(cfg); 122 | return NULL; 123 | } 124 | 125 | continue; 126 | } 127 | 128 | user_value *usr = user_value_parse(key, value, type); 129 | if (usr == NULL) { 130 | config_free(cfg); 131 | return NULL; 132 | } 133 | 134 | user_value *prev = table_swap(&base->native.values, key, usr); 135 | if (prev != NULL) { 136 | user_value_free(prev); 137 | } 138 | } 139 | 140 | base->native.native_lib = sdsdup(cfg->derived.base); 141 | 142 | config_free(cfg); 143 | return base; 144 | } 145 | 146 | typedef struct dl_handle dl_handle; 147 | struct dl_handle { 148 | void *dl; 149 | uprocd_module_entry_type entry; 150 | }; 151 | 152 | int load_dl_handle(const char *module, config *cfg, dl_handle *phandle) { 153 | char *last_slash = strrchr(cfg->path, '/'); 154 | sds native_lib = cfg->native.native_lib ? sdsdup(cfg->native.native_lib) : 155 | sdsnew(module); 156 | sds path; 157 | 158 | if (last_slash == NULL) { 159 | path = sdscat(sdsdup(native_lib), ".so"); 160 | } else { 161 | path = sdsdup(cfg->path); 162 | sdsrange(path, 0, (last_slash - cfg->path - 1)); 163 | path = sdscatfmt(path, "/%S.so", native_lib); 164 | } 165 | sdsfree(native_lib); 166 | 167 | INFO("Loading native library at %S...", path); 168 | void *dl = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); 169 | sdsfree(path); 170 | 171 | if (dl == NULL) { 172 | FAIL("Failed to load native library: %s", dlerror()); 173 | return 0; 174 | } 175 | 176 | void *entry = dlsym(dl, "uprocd_module_entry"); 177 | if (entry == NULL) { 178 | FAIL("Error loading uprocd_module_entry: %s", dlerror()); 179 | dlclose(dl); 180 | return 0; 181 | } 182 | 183 | phandle->dl = dl; 184 | phandle->entry = entry; 185 | return 1; 186 | } 187 | 188 | void interrupt_main(int sig) { 189 | longjmp(global_run_data.return_to_main, sig + 128); 190 | } 191 | 192 | void clear_child(int sig) { 193 | waitpid(-1, NULL, WNOHANG); 194 | } 195 | 196 | int main(int argc, char **argv) { 197 | setproctitle_init(argc, argv); 198 | 199 | if (argc != 3 || argv[1][0] != '+' || argv[1][1] != '\0') { 200 | fprintf(stderr, "uprocd should only be explicitly called by systemd!"); 201 | return 1; 202 | } 203 | 204 | char *module = argv[2]; 205 | setproctitle("-uprocd@%s", module); 206 | 207 | sds module_dir; 208 | config *cfg = load_config(module, &module_dir); 209 | if (cfg == NULL) { 210 | return 1; 211 | } 212 | 213 | if (cfg->kind == CONFIG_DERIVED_MODULE) { 214 | cfg = resolve_derived_config(cfg, &module_dir); 215 | if (cfg == NULL) { 216 | return 1; 217 | } 218 | } 219 | 220 | dl_handle handle; 221 | if (!load_dl_handle(module, cfg, &handle)) { 222 | config_free(cfg); 223 | return 1; 224 | } 225 | 226 | global_run_data.module = module; 227 | global_run_data.module_dir = module_dir; 228 | global_run_data.process_name = cfg->process_name ? sdsdup(cfg->process_name) : NULL; 229 | global_run_data.description = cfg->description ? sdsdup(cfg->description) : NULL; 230 | config_move_out_values(cfg, &global_run_data.config); 231 | global_run_data.exit_handler = NULL; 232 | global_run_data.exit_handler_userdata = NULL; 233 | global_run_data.upcoming_context = NULL; 234 | config_free(cfg); 235 | 236 | int result; 237 | if ((result = setjmp(global_run_data.return_to_main)) == 0) { 238 | INFO("Entering uprocd_run..."); 239 | signal(SIGINT, interrupt_main); 240 | signal(SIGCHLD, clear_child); 241 | result = handle.entry(); 242 | } 243 | 244 | sdsfree(global_run_data.module_dir); 245 | sdsfree(global_run_data.process_name); 246 | sdsfree(global_run_data.description); 247 | return result; 248 | } 249 | -------------------------------------------------------------------------------- /src/uprocd/api.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "private.h" 6 | #include "uprocd.h" 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | extern char **environ; 16 | 17 | UPROCD_EXPORT const char * uprocd_module_directory() { 18 | return global_run_data.module_dir; 19 | } 20 | 21 | UPROCD_EXPORT char * uprocd_module_path(const char *path) { 22 | return sdscatfmt(sdsempty(), "%S/%s", global_run_data.module_dir, path); 23 | } 24 | 25 | UPROCD_EXPORT void uprocd_module_path_free(char *path) { 26 | sdsfree(path); 27 | } 28 | 29 | static user_value *config_get(const char *key) { 30 | return table_get(&global_run_data.config, key); 31 | } 32 | 33 | static int is_index_valid(user_value *usr, int index) { 34 | return index > 0 && index < usr->list.len; 35 | } 36 | 37 | UPROCD_EXPORT int uprocd_config_present(const char *key) { 38 | return config_get(key) != NULL; 39 | } 40 | 41 | UPROCD_EXPORT int uprocd_config_list_size(const char *key) { 42 | user_value *usr = config_get(key); 43 | return usr ? usr->list.len : -1; 44 | } 45 | 46 | UPROCD_EXPORT double uprocd_config_number(const char *key) { 47 | user_value *usr = config_get(key); 48 | return usr ? usr->number : 0; 49 | } 50 | 51 | UPROCD_EXPORT double uprocd_config_number_at(const char *list, 52 | int index) { 53 | user_value *usr = config_get(list); 54 | return usr && is_index_valid(usr, index) ? usr->list.items[index]->number : 0; 55 | } 56 | 57 | UPROCD_EXPORT const char *uprocd_config_string(const char *key) { 58 | user_value *usr = config_get(key); 59 | return usr ? usr->string : NULL; 60 | } 61 | 62 | UPROCD_EXPORT const char *uprocd_config_string_at(const char *list, 63 | int index) { 64 | user_value *usr = config_get(list); 65 | return usr && is_index_valid(usr, index) ? usr->list.items[index]->string : NULL; 66 | } 67 | 68 | struct uprocd_context { 69 | int argc; 70 | sds *argv, *env; 71 | sds cwd; 72 | int fds[3], pid; 73 | }; 74 | 75 | UPROCD_EXPORT void uprocd_context_get_args(uprocd_context *ctx, int *pargc, 76 | char ***pargv) { 77 | *pargc = ctx->argc; 78 | *pargv = ctx->argv; 79 | } 80 | 81 | UPROCD_EXPORT const char ** uprocd_context_get_env(uprocd_context *ctx) { 82 | return (const char **)ctx->env; 83 | } 84 | 85 | UPROCD_EXPORT const char * uprocd_context_get_cwd(uprocd_context *ctx) { 86 | return ctx->cwd; 87 | } 88 | 89 | UPROCD_EXPORT void uprocd_context_free(uprocd_context *ctx) { 90 | for (sds *p = ctx->env; *p; p++) { 91 | sdsfree(*p); 92 | } 93 | free(ctx->env); 94 | for (int i = 0; i < ctx->argc; i++) { 95 | sdsfree(ctx->argv[i]); 96 | } 97 | free(ctx->argv); 98 | sdsfree(ctx->cwd); 99 | close(ctx->fds[0]); 100 | close(ctx->fds[1]); 101 | close(ctx->fds[2]); 102 | free(ctx); 103 | } 104 | 105 | static void move_cgroups(int64_t origin) { 106 | sd_bus *bus = NULL; 107 | sd_bus_message *msg = NULL; 108 | sd_bus_error err = SD_BUS_ERROR_NULL; 109 | int rc; 110 | 111 | rc = sd_bus_open_system(&bus); 112 | if (rc < 0) { 113 | FAIL("sd_bus_open_system failed: %s", strerror(-rc)); 114 | goto end; 115 | } 116 | 117 | int64_t pid = getpid(); 118 | 119 | rc = sd_bus_call_method(bus, "com.refi64.uprocd.Cgrmvd", 120 | "/com/refi64/uprocd/Cgrmvd", "com.refi64.uprocd.Cgrmvd", 121 | "MoveCgroup", &err, &msg, "xx", pid, origin); 122 | if (rc < 0) { 123 | FAIL("Error calling com.refi64.uprocd.Cgrmvd.MoveCgroup: %s", err.message); 124 | goto end; 125 | } 126 | 127 | end: 128 | sd_bus_error_free(&err); 129 | sd_bus_message_unref(msg); 130 | sd_bus_unref(bus); 131 | } 132 | 133 | UPROCD_EXPORT void uprocd_context_enter(uprocd_context *ctx) { 134 | for (char **p = environ; *p; p++) { 135 | sds env = sdsnew(*p); 136 | sds eq = strchr(env, '='); 137 | sdsrange(env, 0, eq - env - 1); 138 | unsetenv(env); 139 | sdsfree(env); 140 | } 141 | 142 | for (sds *p = ctx->env; *p; p+=2) { 143 | setenv(p[0], p[1], 1); 144 | } 145 | 146 | if (chdir(ctx->cwd) == -1) { 147 | FAIL("WARNING: chdir into new cwd %S failed: %s", ctx->cwd, strerror(errno)); 148 | } 149 | 150 | close(0); 151 | close(1); 152 | close(2); 153 | dup2(ctx->fds[0], 0); 154 | dup2(ctx->fds[1], 1); 155 | dup2(ctx->fds[2], 2); 156 | 157 | move_cgroups(ctx->pid); 158 | 159 | if (setpgrp() == -1) { 160 | FAIL("WARNING: setpgrp failed: %s", strerror(errno)); 161 | } 162 | } 163 | 164 | sds * convert_env_to_api_format(table *penv) { 165 | sds *entries = newa(sds, penv->sz * 2 + 1), *current = entries; 166 | char *name = NULL, *value; 167 | while ((name = table_next(penv, name, (void**)&value))) { 168 | *(current++) = sdsnew(name); 169 | *(current++) = sdsnew(value); 170 | } 171 | 172 | *current = NULL; 173 | return entries; 174 | } 175 | 176 | int prepare_context_and_fork(int argc, char **argv, table *env, char *cwd, int *fds, 177 | pid_t pid) { 178 | int wait_for_set_ptracer[2]; 179 | if (pipe(wait_for_set_ptracer) == -1) { 180 | FAIL("Error creating pipe to wait for prctl: %s", strerror(errno)); 181 | return -errno; 182 | } 183 | 184 | sds *entries = convert_env_to_api_format(env); 185 | 186 | uprocd_context *ctx = new(uprocd_context); 187 | ctx->argc = argc; 188 | ctx->argv = argv; 189 | ctx->env = entries; 190 | ctx->cwd = sdsnew(cwd); 191 | ctx->fds[0] = dup(fds[0]); 192 | ctx->fds[1] = dup(fds[1]); 193 | ctx->fds[2] = dup(fds[2]); 194 | ctx->pid = pid; 195 | global_run_data.upcoming_context = ctx; 196 | 197 | pid_t child = fork(); 198 | if (child == -1) { 199 | FAIL("fork failed: %s", strerror(errno)); 200 | return -errno; 201 | } else if (child == 0) { 202 | prctl(PR_SET_PTRACER, pid, 0, 0); 203 | ioctl(0, TIOCSCTTY, 1); 204 | 205 | write(wait_for_set_ptracer[1], "", 1); 206 | close(wait_for_set_ptracer[0]); 207 | close(wait_for_set_ptracer[1]); 208 | 209 | setproctitle("-uprocd:%s", global_run_data.module); 210 | signal(SIGINT, SIG_DFL); 211 | signal(SIGCHLD, SIG_DFL); 212 | 213 | return 0; 214 | } else { 215 | char byte; 216 | read(wait_for_set_ptracer[0], &byte, 1); 217 | close(wait_for_set_ptracer[0]); 218 | close(wait_for_set_ptracer[1]); 219 | 220 | return child; 221 | } 222 | } 223 | 224 | UPROCD_EXPORT void uprocd_on_exit(uprocd_exit_handler func, void *userdata) { 225 | global_run_data.exit_handler = func; 226 | global_run_data.exit_handler_userdata = userdata; 227 | } 228 | 229 | UPROCD_EXPORT uprocd_context * uprocd_run() { 230 | int rc; 231 | bus_data *data = NULL; 232 | 233 | if (setjmp(global_run_data.return_to_loop) != 0) { 234 | bus_free(data); 235 | return global_run_data.upcoming_context; 236 | } 237 | 238 | data = bus_new(); 239 | if (data == NULL) { 240 | goto failure; 241 | } 242 | 243 | for (;;) { 244 | rc = bus_pump(data); 245 | if (rc < 0) { 246 | goto failure; 247 | } 248 | else if (rc == 1) { 249 | free(global_run_data.upcoming_context); 250 | global_run_data.upcoming_context = NULL; 251 | } 252 | } 253 | 254 | failure: 255 | bus_free(data); 256 | if (global_run_data.exit_handler) { 257 | uprocd_exit_handler handler = global_run_data.exit_handler; 258 | handler(global_run_data.exit_handler_userdata); 259 | } 260 | longjmp(global_run_data.return_to_main, 1); 261 | } 262 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | uprocd 3 | 4 | 5 | 6 | 91 | 92 | 93 | 94 |
95 |

uprocd

96 |
97 | 98 |
99 | 100 |
101 | Git 102 | Bugs 103 | Mail 104 | Discord 105 | man 106 |
107 | 108 |
109 | 110 |

111 | uprocd is a "process preloader" or "process cache" for Linux systems. It starts 112 | a process (referred to as a module) in the background, then freezes it once 113 | initialization is completed. When you ask uprocd to run that module, it forks it and 114 | attaches the forked version to your terminal. This means that processes are only 115 | initialized once, so you don't have to wait around after starting a process.

116 | 117 |

118 | In other words: many programs will have a "daemon mode", where a copy of the program 119 | is running in the background, in order to improve startup speed. uprocd uses the 120 | same principle, except it is designed to be more generic: any application author 121 | can easily create a uprocd module in order to make their application's start-up 122 | time faster.

123 | 124 |

Want proof that it works?

125 | 126 |

127 | # IPython
128 |  ryan@DevPC-archLX  ~  /usr/bin/time -f 'Time elapsed: %E' ipython -h > /dev/null
129 | Time elapsed: 0:00.60
130 |  ryan@DevPC-archLX  ~  /usr/bin/time -f 'Time elapsed: %E' u ipython -h > /dev/null
131 | Time elapsed: 0:00.15
132 | # Mypy
133 |  ryan@DevPC-archLX  ~  /usr/bin/time -f 'Time elapsed: %E' mypy -h > /dev/null
134 | Time elapsed: 0:00.52
135 |  ryan@DevPC-archLX  ~  /usr/bin/time -f 'Time elapsed: %E' umypy -h > /dev/null
136 | Time elapsed: 0:00.11
137 |   
138 | 139 |

140 | The uprocd version is faster because a copy of IPython was already running. That 141 | copy was just forked and attached to the current terminal.

142 | 143 |

Getting Started

144 | 145 |
146 | 147 |

Requirements

148 | 149 |

You need:

150 | 151 |
    152 |
  • A C compiler.
  • 153 |
  • 154 | Python 3 and 155 | Fbuild 0.3 RC2 or greater for 156 | building (just use the master branch).
  • 157 |
  • Judy arrays.
  • 158 |
  • 159 | A systemd-powered 160 | Linux system. uprocd itself only spawns daemons for each module and manages 161 | communication with them. systemd is used for both managing the daemons (via 162 | 163 | systemctl) and internally used for communication (via 164 | sd-bus). 165 |
  • 166 |
  • 167 | Optional: mrkd if you want 168 | to build the man pages.
  • 169 |
  • 170 | Optional: CPython 3 development files 171 | if you want to build the Python and IPython 172 | modules.
  • 173 |
  • 174 | Optional: Ruby for building 175 | the Ruby module.
  • 176 |
177 | 178 |

Downloading and Building

179 | 180 |

Just run:

181 | 182 |

183 | $ git clone --recursive https://github.com/kirbyfan64/uprocd.git
184 | $ fbuild
185 | $ sudo fbuild install
186 | # Enable the cgrmvd system service.
187 | $ systemctl enable cgrmvd
188 | $ systemctl start cgrmvd
189 |   
190 | 191 |

Use fbuild --release for a release build.

192 | 193 |

Basic Usage

194 | 195 |
196 | 197 |

198 | # Start the uprocd Python module. This will start Python in the background, then
199 | # freeze it until later.
200 | $ systemctl --user start uprocd@python
201 | # Have the module be automatically started at every boot.
202 | $ systemctl --user enable uprocd@python
203 | 
204 | # Run Python via uprocd. This will fork the background process and bring the forked
205 | # one to the foreground.
206 | $ uprocctl run python -h
207 | # u is a shortcut for uprocctl run
208 | $ u python -h
209 | # upython is an included shortcut for u python
210 | $ upython -h
211 | 
212 | # In fact, any symlink to uprocctl beginning with the letter u is a shortcut. e.g.
213 | # a symlink named uipython is a shortcut for uprocctl run ipython:
214 | $ uipython
215 | # Same as:
216 | $ u ipython
217 | # Same as:
218 | $ uprocctl run ipython
219 |   
220 | 221 |

222 | For more information, see the man pages via man uprocd.index, or 223 | view them online.

224 | 225 |

Extending

226 | 227 |

228 | Again, see the man pages for detailed information. If you want to get started, 229 | check out 230 | the source code 231 | for the both the Python modules and two basic examples. 232 | 233 |

FAQs

234 | 235 |
236 | 237 |

Are there other similar projects?

238 | 239 |

240 | Android's 241 | Zygote does something similar to ensure the VM is already warmed up: it loads 242 | once, then forks off into other processes with an already-warmed VM.

243 | 244 |

245 | I was also recently informed that 246 | KDE2 247 | did something similar with kdeinit in order to avoid loading shared libraries 248 | over and over again.

249 | 250 |

How does uprocd affect memory usage?

251 | 252 |

253 | It shouldn't affect it that much. For instance, leaving uprocd's IPython module 254 | running in the background won't affect your memory usage much more than keeping 255 | IPython open all the time.

256 | 257 |

258 | Now in theory, uprocd could actually decrease memory usage: since forks are 259 | copy-on-write, any memory that isn't written two would be shared between processes. 260 | However, all the current uprocd modules (Python and Ruby) use interpreted, 261 | garbage-collected languages, and the second the GC touches a memory page, it will be 262 | copied.

263 | 264 |

265 | If a uprocd module were created for program written in a non-GC'd language, it would 266 | be more likely to show decreased memory usage.

267 | 268 |

Does enabling uprocd modules at boot increase boot times?

269 | 270 |

271 | Yes. In theory, this isn't much of a loss: systemd's parallelism helps ensure that 272 | uprocd alone won't hold back your entire boot, and I personally haven't noticed 273 | any significant slowdowns.

274 | 275 |

276 | That being said, if you want the stats: the Python modules each take around 2s to 277 | start. Not sure about Ruby, since I don't have it enabled on boot. For comparison, 278 | Docker takes a little over 3s.

279 | 280 |

Contributing

281 | 282 |

283 | Check out the GitHub repo for 284 | the source code. Extra modules that are either really useful or really cool can 285 | be suggested for inclusion.

286 | 287 |

Also be sure to file any bugs there!

288 | 289 | -------------------------------------------------------------------------------- /src/cgrmvd/cgrmvd.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void _fail(sds message) { 16 | int errno_ = errno; 17 | 18 | fprintf(stderr, SD_CRIT "%.*s\n", (int)sdslen(message), message); 19 | sd_notifyf(0, "STATUS=\"Failure: %s\"", message); 20 | sdsfree(message); 21 | 22 | errno = errno_; 23 | } 24 | 25 | void _busfail(sd_bus_error *err, sds message) { 26 | _fail(sdsdup(message)); 27 | sd_bus_error_set(err, "com.refi64.cgrmvd.Error", message); 28 | sdsfree(message); 29 | } 30 | 31 | #define FMT(...) sdscatfmt(sdsempty(), __VA_ARGS__) 32 | #define FAIL(...) _fail(FMT(__VA_ARGS__)) 33 | #define BUSFAIL(err, ...) _busfail(err, FMT(__VA_ARGS__)) 34 | 35 | typedef struct policy_origins { 36 | sds *origins; 37 | int len; 38 | } policy_origins; 39 | 40 | table g_policies; 41 | 42 | void free_policy(policy_origins *policy) { 43 | sdsfreesplitres(policy->origins, policy->len); 44 | } 45 | 46 | void read_policy(sds path) { 47 | FILE *fp = fopen(path, "r"); 48 | if (fp == NULL) { 49 | FAIL("Error opening %S: %s", path, strerror(errno)); 50 | return; 51 | } 52 | 53 | sds line = NULL; 54 | int rc = 0, lineno = 0; 55 | 56 | while ((rc = readline(fp, &line)) == 0 && line != NULL) { 57 | lineno++; 58 | sdstrim(line, "\t "); 59 | 60 | size_t len = sdslen(line); 61 | if (len == 0 || line[0] == '#') { 62 | goto parse_end; 63 | } 64 | 65 | char *mid = strstr(line, " : "); 66 | if (mid == NULL) { 67 | FAIL("Error parsing %S:%d."); 68 | goto parse_end; 69 | } 70 | 71 | sds copier = sdsdup(line), origins = sdsdup(line); 72 | sdsrange(copier, 0, mid - line - 1); 73 | sdsrange(origins, mid - line + 3, -1); 74 | 75 | policy_origins *policy = new(policy_origins); 76 | policy->origins = sdssplitlen(origins, sdslen(origins), " ", 1, &policy->len); 77 | 78 | policy_origins *original = table_swap(&g_policies, copier, policy); 79 | if (original != NULL) { 80 | FAIL("WARNING: Copier %s has multiple origin values", copier); 81 | free_policy(original); 82 | } 83 | 84 | sdsfree(copier); 85 | sdsfree(origins); 86 | 87 | parse_end: 88 | sdsfree(line); 89 | } 90 | 91 | fclose(fp); 92 | } 93 | 94 | void reload_policies() { 95 | table_free(&g_policies); 96 | table_init(&g_policies); 97 | 98 | DIR *dir; 99 | struct dirent *entry; 100 | 101 | char *root = "/usr/share/cgrmvd/policies"; 102 | dir = opendir(root); 103 | if (dir == NULL) { 104 | FAIL("Error opening %s: %s", root, strerror(errno)); 105 | return; 106 | } 107 | 108 | for (;;) { 109 | errno = 0; 110 | entry = readdir(dir); 111 | if (entry == NULL) { 112 | break; 113 | } 114 | 115 | if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { 116 | continue; 117 | } 118 | 119 | sds name = sdsnew(entry->d_name); 120 | int len = sdslen(name); 121 | if (len < 8 || strcmp(name + len - 7, ".policy") != 0) { 122 | FAIL("Invalid file path (expected .policy): %S", name); 123 | goto loop_end; 124 | } 125 | 126 | sds path = sdscatfmt(sdsnew(root), "/%S", name); 127 | read_policy(path); 128 | sdsfree(path); 129 | 130 | loop_end: 131 | sdsfree(name); 132 | } 133 | 134 | closedir(dir); 135 | } 136 | 137 | int readlink_bus(sds path, sds *out, sd_bus_error *err) { 138 | char buf[PATH_MAX + 1]; 139 | ssize_t sz = readlink(path, buf, sizeof(buf) - 1); 140 | 141 | if (sz == -1) { 142 | BUSFAIL(err, "Error reading link behind %S: %s", path, strerror(errno)); 143 | sdsfree(path); 144 | return -errno; 145 | } 146 | 147 | *out = sdsnewlen(buf, sz); 148 | sdsfree(path); 149 | 150 | struct stat st; 151 | if (lstat(*out, &st) == -1) { 152 | FAIL("WARNING: lstat on %S from readlink_bus failed: %s", *out, strerror(errno)); 153 | } else { 154 | if (S_ISLNK(st.st_mode)) { 155 | return readlink_bus(sdsdup(*out), out, err); 156 | } 157 | } 158 | 159 | return 0; 160 | } 161 | 162 | int fopen_bus(sds path, FILE **out, char *mode, sd_bus_error *err) { 163 | *out = fopen(path, mode); 164 | 165 | if (*out == NULL) { 166 | BUSFAIL(err, "Error reading %S: %s", path, strerror(errno)); 167 | sdsfree(path); 168 | return -errno; 169 | } 170 | 171 | sdsfree(path); 172 | return 0; 173 | } 174 | 175 | int verify_policy(int64_t copier, int64_t origin, sd_bus_error *err) { 176 | sds copier_exe, origin_exe; 177 | int rc; 178 | 179 | rc = readlink_bus(sdscatfmt(sdsempty(), "/proc/%I/exe", copier), &copier_exe, err); 180 | if (rc < 0) { 181 | return rc; 182 | } 183 | 184 | rc = readlink_bus(sdscatfmt(sdsempty(), "/proc/%I/exe", origin), &origin_exe, err); 185 | if (rc < 0) { 186 | sdsfree(copier_exe); 187 | return rc; 188 | } 189 | 190 | policy_origins *policy = table_get(&g_policies, copier_exe); 191 | if (policy == NULL) { 192 | BUSFAIL(err, "Policy for %S does not exist.", copier_exe); 193 | return -EPERM; 194 | } 195 | 196 | int origin_match = 0; 197 | for (int i = 0; i < policy->len; i++) { 198 | if (strcmp(origin_exe, policy->origins[i]) == 0) { 199 | origin_match = 1; 200 | break; 201 | } 202 | } 203 | 204 | if (!origin_match) { 205 | BUSFAIL(err, "Policy for %S does not include origin %S.", copier_exe, origin_exe); 206 | return -EPERM; 207 | } 208 | 209 | return 0; 210 | } 211 | 212 | int parse_cgroup_path(int64_t pid, FILE *fp, sds *path, sd_bus_error *err) { 213 | sds line = NULL; 214 | int rc = 0, nparts = 0; 215 | sds *parts; 216 | 217 | rc = readline(fp, &line); 218 | if (rc < 0) { 219 | BUSFAIL(err, "Error reading line from /proc/%I/cgroup: %s", pid, strerror(errno)); 220 | return -errno; 221 | } 222 | 223 | if (line == NULL) { 224 | *path = NULL; 225 | return 0; 226 | } 227 | 228 | parts = sdssplitlen(line, sdslen(line), ":", 1, &nparts); 229 | if (nparts != 3) { 230 | BUSFAIL(err, "Invalid line in /proc/%I/cgroup: %S", pid, line); 231 | rc = -EINVAL; 232 | goto end; 233 | } 234 | 235 | if (strncmp(parts[1], "name=", 5) == 0) { 236 | sdsrange(parts[1], 5, -1); 237 | } 238 | 239 | if (sdslen(parts[1]) == 0) { 240 | sdsfree(parts[1]); 241 | parts[1] = sdsnew("unified"); 242 | } 243 | 244 | *path = sdscatfmt(sdsempty(), "/sys/fs/cgroup/%S%S", parts[1], parts[2]); 245 | if ((*path)[sdslen(*path) - 1] == '/') { 246 | sdsrange(*path, 0, -2); 247 | } 248 | 249 | end: 250 | sdsfree(line); 251 | 252 | if (parts) { 253 | sdsfreesplitres(parts, nparts); 254 | } 255 | return rc; 256 | } 257 | 258 | int move_cgroups(int64_t copier, int64_t origin, sd_bus_error *err) { 259 | FILE *copier_fp = NULL, *origin_fp = NULL; 260 | int rc = 0; 261 | 262 | rc = fopen_bus(sdscatfmt(sdsempty(), "/proc/%I/cgroup", copier), &copier_fp, "r", err); 263 | if (rc < 0) { 264 | goto end; 265 | } 266 | 267 | rc = fopen_bus(sdscatfmt(sdsempty(), "/proc/%I/cgroup", origin), &origin_fp, "r", err); 268 | if (rc < 0) { 269 | goto end; 270 | } 271 | 272 | for (;;) { 273 | sds copier_path = NULL, origin_path = NULL; 274 | 275 | rc = parse_cgroup_path(origin, origin_fp, &origin_path, err); 276 | if (rc < 0 || origin_path == NULL) { 277 | goto end; 278 | } 279 | 280 | rc = parse_cgroup_path(copier, copier_fp, &copier_path, err); 281 | if (rc < 0 || copier_path == NULL) { 282 | sdsfree(origin_path); 283 | goto end; 284 | } 285 | 286 | if (strcmp(origin_path, copier_path) == 0) { 287 | goto loop_end; 288 | } 289 | 290 | sds target = sdscat(sdsdup(origin_path), "/tasks"); 291 | if (access(target, W_OK) != 0) { 292 | sdsfree(target); 293 | target = sdscat(sdsdup(origin_path), "/cgroup.procs"); 294 | if (access(target, W_OK) != 0) { 295 | sdsfree(target); 296 | BUSFAIL(err, "Neither %S/tasks or %S/cgroup.procs are writable.", copier_path, 297 | copier_path); 298 | rc = -EACCES; 299 | goto loop_end; 300 | } 301 | } 302 | 303 | FILE *target_fp; 304 | rc = fopen_bus(target, &target_fp, "wa", err); 305 | if (rc < 0) { 306 | goto loop_end; 307 | } 308 | 309 | fprintf(target_fp, "%ld\n", copier); 310 | fclose(target_fp); 311 | 312 | loop_end: 313 | sdsfree(copier_path); 314 | sdsfree(origin_path); 315 | if (rc < 0) { 316 | break; 317 | } 318 | } 319 | 320 | end: 321 | if (copier_fp) { 322 | fclose(copier_fp); 323 | } 324 | if (origin_fp) { 325 | fclose(origin_fp); 326 | } 327 | return rc; 328 | } 329 | 330 | int service_method_move_cgroup(sd_bus_message *msg, void *data, sd_bus_error *err) { 331 | int64_t copier, origin; 332 | int rc; 333 | 334 | rc = sd_bus_message_read(msg, "xx", &copier, &origin); 335 | if (rc < 0) { 336 | FAIL("Error parsing bus message: %s", strerror(-rc)); 337 | return rc; 338 | } 339 | 340 | rc = verify_policy(copier, origin, err); 341 | if (rc < 0) { 342 | return rc; 343 | } 344 | 345 | rc = move_cgroups(copier, origin, err); 346 | if (rc < 0) { 347 | return rc; 348 | } 349 | 350 | return sd_bus_reply_method_return(msg, ""); 351 | } 352 | 353 | static const sd_bus_vtable service_vtable[] = { 354 | SD_BUS_VTABLE_START(0), 355 | // MoveCgroup(Int64 copier_pid, Int64 origin_pid) 356 | SD_BUS_METHOD("MoveCgroup", "xx", "", service_method_move_cgroup, 357 | SD_BUS_VTABLE_UNPRIVILEGED), 358 | SD_BUS_VTABLE_END 359 | }; 360 | 361 | void bus_loop() { 362 | // XXX: This code is similar to uprocd/bus.c. 363 | int rc; 364 | sd_bus *bus = NULL; 365 | sd_bus_slot *slot = NULL; 366 | 367 | rc = sd_bus_open_system(&bus); 368 | if (rc < 0) { 369 | FAIL("sd_bus_open_user failed: %s", strerror(-rc)); 370 | goto end; 371 | } 372 | 373 | rc = sd_bus_add_object_vtable(bus, &slot, "/com/refi64/uprocd/Cgrmvd", 374 | "com.refi64.uprocd.Cgrmvd", service_vtable, NULL); 375 | if (rc < 0) { 376 | FAIL("sd_bus_add_object_vtable failed: %s", strerror(-rc)); 377 | goto end; 378 | } 379 | 380 | rc = sd_bus_request_name(bus, "com.refi64.uprocd.Cgrmvd", 0); 381 | if (rc < 0) { 382 | FAIL("sd_bus_request_name failed: %s", strerror(-rc)); 383 | goto end; 384 | } 385 | 386 | for (;;) { 387 | rc = sd_bus_process(bus, NULL); 388 | if (rc < 0) { 389 | FAIL("sd_bus_process failed: %s", strerror(-rc)); 390 | goto end; 391 | } else if (rc > 0) { 392 | continue; 393 | } 394 | 395 | rc = sd_bus_wait(bus, (uint64_t)-1); 396 | if (rc < 0 && rc != -EINTR) { 397 | FAIL("sd_bus_wait failed: %s", strerror(-rc)); 398 | goto end; 399 | } 400 | } 401 | 402 | end: 403 | sd_bus_slot_unref(slot); 404 | sd_bus_unref(bus); 405 | } 406 | 407 | int main(int argc, char **argv) { 408 | table_init(&g_policies); 409 | 410 | signal(SIGHUP, reload_policies); 411 | reload_policies(); 412 | 413 | bus_loop(); 414 | } 415 | -------------------------------------------------------------------------------- /src/uprocctl/main.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include "common.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | extern char **environ; 15 | int64_t target_pid = -1; 16 | 17 | #define STATUS_USAGE "status [-h] module" 18 | #define RUN_USAGE "run [-h] module [args...]" 19 | #define U_USAGE "[-h] module [args...]" 20 | 21 | void _fail(sds message) { 22 | fprintf(stderr, "uprocctl: %s\n", message); 23 | sdsfree(message); 24 | } 25 | 26 | #define FAIL(...) _fail(sdscatfmt(sdsempty(), __VA_ARGS__)) 27 | 28 | char * last_path_component(char *path) { 29 | char *p = strrchr(path, '/'); 30 | return p ? p + 1 : path; 31 | } 32 | 33 | void usage() { 34 | puts("usage: uprocctl -h"); 35 | puts(" uprocctl " STATUS_USAGE); 36 | puts(" uprocctl " RUN_USAGE); 37 | puts(" u " U_USAGE); 38 | } 39 | 40 | void status_usage() { 41 | puts("usage: uprocctl " STATUS_USAGE); 42 | } 43 | 44 | void run_usage() { 45 | puts("usage: uprocctl " RUN_USAGE); 46 | } 47 | 48 | void u_usage() { 49 | puts("usage: u " U_USAGE); 50 | } 51 | 52 | void help() { 53 | puts("uprocctl allows you to communicate with uprocd modules."); 54 | puts(""); 55 | puts("Commands:"); 56 | puts(""); 57 | puts(" status Show the status of a uprocd module."); 58 | puts(" run Run a command through a uprocd module."); 59 | puts(""); 60 | puts("The u command is a shortcut for uprocctl run."); 61 | } 62 | 63 | void status_help() { 64 | puts("uprocctl status shows the status of the given uprocd module."); 65 | puts(""); 66 | puts(" -h Show this screen."); 67 | puts(" module The uprocd module to retrieve information for."); 68 | } 69 | 70 | void run_help_base(int u) { 71 | puts("uprocctl run allows you to spawn commands via the uprocd modules."); 72 | if (u) { 73 | puts("u is a shortcut for uprocctl run."); 74 | } 75 | puts(""); 76 | puts(" -h Show this screen."); 77 | puts(" module The uprocd module to run."); 78 | puts(" [args...] Command line arguments to pass to the module."); 79 | } 80 | 81 | void run_help() { 82 | run_help_base(0); 83 | } 84 | 85 | void u_help() { 86 | run_help_base(1); 87 | } 88 | 89 | void killme(int sig) { 90 | signal(sig, SIG_DFL); 91 | if (raise(sig) != 0) { 92 | FAIL("raise failed: %s", strerror(errno)); 93 | abort(); 94 | } 95 | } 96 | 97 | int status(const char *module) { 98 | sd_bus *bus = NULL; 99 | sd_bus_message *msg = NULL, *reply = NULL; 100 | sd_bus_error err = SD_BUS_ERROR_NULL; 101 | int rc; 102 | 103 | rc = sd_bus_open_user(&bus); 104 | if (rc < 0) { 105 | FAIL("sd_bus_open_user failed: %s", strerror(-rc)); 106 | goto end; 107 | } 108 | 109 | sds service, object; 110 | get_bus_params(module, &service, &object); 111 | 112 | rc = sd_bus_message_new_method_call(bus, &msg, service, object, service, "Status"); 113 | if (rc < 0) { 114 | FAIL("sd_bus_message_new_method_call failed: %s", strerror(-rc)); 115 | goto end; 116 | } 117 | 118 | rc = sd_bus_call(bus, msg, 0, &err, &reply); 119 | if (rc < 0) { 120 | FAIL("sd_bus_call failed: %s", err.message); 121 | FAIL("Are you sure the uprocd module has been started?"); 122 | goto end; 123 | } 124 | 125 | char *name, *description; 126 | rc = sd_bus_message_read(reply, "ss", &name, &description); 127 | if (rc < 0) { 128 | FAIL("uprocd process bus failed to return status information."); 129 | goto end; 130 | } 131 | 132 | printf("Name: %s\n", name); 133 | printf("Description: %s\n", description); 134 | 135 | end: 136 | sd_bus_error_free(&err); 137 | if (msg) { 138 | sd_bus_message_unref(msg); 139 | } 140 | if (bus) { 141 | sd_bus_unref(bus); 142 | } 143 | 144 | if (rc < 0) { 145 | return 1; 146 | } else { 147 | return 0; 148 | } 149 | } 150 | 151 | int wait_for_process() { 152 | if (ptrace(PTRACE_SEIZE, target_pid, NULL, 153 | (void*)(PTRACE_O_EXITKILL | PTRACE_O_TRACEEXIT)) == -1) { 154 | FAIL("Error seizing %I via ptrace: %s", target_pid, strerror(errno)); 155 | return 1; 156 | } 157 | 158 | for (;;) { 159 | int wstatus; 160 | if (waitpid(target_pid, &wstatus, 0) == -1) { 161 | FAIL("waitpid: %s", strerror(errno)); 162 | return 1; 163 | } 164 | 165 | if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { 166 | unsigned long pstatus; 167 | if (ptrace(PTRACE_GETEVENTMSG, target_pid, NULL, &pstatus) == -1) { 168 | FAIL("PTRACE_GETEVENTMSG failed: %s", strerror(errno)); 169 | return 0; 170 | } 171 | 172 | return WEXITSTATUS(pstatus); 173 | } else if (WIFSTOPPED(wstatus) || WIFSIGNALED(wstatus)) { 174 | killme(WIFSTOPPED(wstatus) ? WSTOPSIG(wstatus) : WTERMSIG(wstatus)); 175 | return 0; 176 | } else if (WIFEXITED(wstatus)) { 177 | return WEXITSTATUS(wstatus); 178 | } else { 179 | FAIL("waitpid gave unexpected status: %d\n", wstatus); 180 | } 181 | } 182 | } 183 | 184 | void forward_signal(int sig) { 185 | if (target_pid < 1) { 186 | FAIL("target_pid == %I in forward_signal", target_pid); 187 | abort(); 188 | } 189 | kill(target_pid, sig); 190 | } 191 | 192 | int run(char *module, int argc, char **argv) { 193 | sd_bus *bus = NULL; 194 | sd_bus_message *msg = NULL, *reply = NULL; 195 | sd_bus_error err = SD_BUS_ERROR_NULL; 196 | int rc; 197 | char *title; 198 | 199 | char *cwd = getcwd(NULL, 0); 200 | if (cwd == NULL) { 201 | FAIL("Error retrieving current working directory: %s", strerror(errno)); 202 | rc = -errno; 203 | goto end; 204 | } 205 | 206 | rc = sd_bus_open_user(&bus); 207 | if (rc < 0) { 208 | FAIL("sd_bus_open_user failed: %s", strerror(-rc)); 209 | goto end; 210 | } 211 | 212 | sds service, object; 213 | get_bus_params(module, &service, &object); 214 | 215 | rc = sd_bus_message_new_method_call(bus, &msg, service, object, service, "Run"); 216 | if (rc < 0) { 217 | FAIL("sd_bus_message_new_method_call failed: %s", strerror(-rc)); 218 | goto end; 219 | } 220 | 221 | rc = sd_bus_message_open_container(msg, 'a', "{ss}"); 222 | if (rc < 0) { 223 | goto write_end; 224 | } 225 | 226 | for (char **p = environ; *p; p++) { 227 | rc = sd_bus_message_open_container(msg, 'e', "ss"); 228 | if (rc < 0) { 229 | goto write_end; 230 | } 231 | 232 | sds env = sdsnew(*p), key = sdsdup(env), value = sdsdup(env); 233 | sds eq = strchr(env, '='); 234 | sdsrange(key, 0, eq - env - 1); 235 | sdsrange(value, eq - env + 1, -1); 236 | 237 | rc = sd_bus_message_append(msg, "ss", key, value); 238 | sdsfree(key); 239 | sdsfree(value); 240 | if (rc < 0) { 241 | goto write_end; 242 | } 243 | 244 | rc = sd_bus_message_close_container(msg); 245 | if (rc < 0) { 246 | goto write_end; 247 | } 248 | } 249 | 250 | rc = sd_bus_message_close_container(msg); 251 | if (rc < 0) { 252 | goto write_end; 253 | } 254 | 255 | rc = sd_bus_message_open_container(msg, 'a', "s"); 256 | if (rc < 0) { 257 | goto write_end; 258 | } 259 | 260 | for (int i = 0; i < argc; i++) { 261 | rc = sd_bus_message_append_basic(msg, 's', argv[i]); 262 | if (rc < 0) { 263 | goto write_end; 264 | } 265 | } 266 | 267 | rc = sd_bus_message_close_container(msg); 268 | if (rc < 0) { 269 | goto write_end; 270 | } 271 | 272 | rc = sd_bus_message_append(msg, "s(hhh)x", cwd, dup(0), dup(1), dup(2), getpid()); 273 | if (rc < 0) { 274 | goto write_end; 275 | } 276 | 277 | write_end: 278 | if (rc < 0) { 279 | FAIL("Error writing bus message: %s", strerror(-rc)); 280 | goto end; 281 | } 282 | 283 | rc = sd_bus_call(bus, msg, 0, &err, &reply); 284 | if (rc < 0) { 285 | if (strcmp(err.name, SD_BUS_ERROR_SERVICE_UNKNOWN) == 0) { 286 | FAIL("Failed to locate %s's D-Bus service.", module); 287 | FAIL("Are you sure it has been started? (Try systemctl --user status uprocd@%s.)", 288 | module); 289 | } else { 290 | FAIL("sd_bus_call failed: %s", err.message); 291 | FAIL("Are you sure the uprocd module has been started?"); 292 | } 293 | goto end; 294 | } 295 | 296 | rc = sd_bus_message_read(reply, "xs", &target_pid, &title); 297 | if (rc < 0) { 298 | FAIL("uprocd process bus failed to return the new PID."); 299 | goto end; 300 | } 301 | 302 | setproctitle("-%s", title); 303 | 304 | end: 305 | if (cwd) { 306 | free(cwd); 307 | } 308 | sd_bus_error_free(&err); 309 | if (msg) { 310 | sd_bus_message_unref(msg); 311 | } 312 | if (bus) { 313 | sd_bus_unref(bus); 314 | } 315 | 316 | if (rc < 0) { 317 | return 1; 318 | } else { 319 | for (int sig = 0; sig < 31; sig++) { 320 | if (sig == SIGCHLD) { 321 | continue; 322 | } 323 | signal(sig, forward_signal); 324 | } 325 | 326 | return wait_for_process(); 327 | } 328 | } 329 | 330 | int check_command(const char *command, int argc, char **argv, int exact, 331 | void (*usage)(), void (*help)()) { 332 | int correct_number = exact ? argc == 1 : argc >= 1; 333 | if (!correct_number) { 334 | if (exact) { 335 | FAIL("%s requires exactly one argument.", command); 336 | } else { 337 | FAIL("%s requires at least one argument.", command); 338 | } 339 | usage(); 340 | return -1; 341 | } 342 | 343 | int is_help = strcmp(argv[0], "-h") == 0; 344 | if (argv[0][0] == '-' && !is_help) { 345 | FAIL("Invalid argument: %s.", argv[0]); 346 | usage(); 347 | return -1; 348 | } 349 | 350 | if (is_help) { 351 | usage(); 352 | putchar('\n'); 353 | help(); 354 | return 1; 355 | } 356 | 357 | return 0; 358 | } 359 | 360 | int main(int argc, char **argv) { 361 | setproctitle_init(argc, argv); 362 | 363 | char *name = last_path_component(argv[0]); 364 | 365 | if (strcmp(name, "u") == 0) { 366 | int rc = check_command("u", argc - 1, argv + 1, 0, u_usage, u_help); 367 | if (rc) { 368 | return rc < 0 ? 1 : 0; 369 | } 370 | return run(argv[1], argc - 2, argv + 2); 371 | } else if (strncmp(name, "u", 1) == 0 && strcmp(name, "uprocctl") != 0) { 372 | return run(name + 1, argc - 1, argv + 1); 373 | } else { 374 | if (strcmp(name, "uprocctl") != 0) { 375 | FAIL("WARNING: argv[0] is not a recognized uprocd symlink. Assuming uprocctl..."); 376 | } 377 | 378 | if (argc < 2) { 379 | FAIL("An argument is required."); 380 | usage(); 381 | return 1; 382 | } 383 | 384 | if (strcmp(argv[1], "-h") == 0) { 385 | usage(); 386 | putchar('\n'); 387 | help(); 388 | return 0; 389 | } else if (strcmp(argv[1], "status") == 0) { 390 | int rc = check_command("status", argc - 2, argv + 2, 1, status_usage, status_help); 391 | if (rc) { 392 | return rc < 0 ? 1 : 0; 393 | } 394 | return status(argv[2]); 395 | } else if (strcmp(argv[1], "run") == 0) { 396 | int rc = check_command("run", argc - 2, argv + 2, 0, run_usage, run_help); 397 | if (rc) { 398 | return rc < 0 ? 1 : 0; 399 | } 400 | return run(argv[2], argc - 3, argv + 3); 401 | } else { 402 | FAIL("Invalid command."); 403 | usage(); 404 | return 1; 405 | } 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /fbuildroot.py: -------------------------------------------------------------------------------- 1 | from fbuild.builders.platform import guess_platform 2 | from fbuild.builders.pkg_config import PkgConfig 3 | from fbuild.builders.file import copy 4 | from fbuild.builders.c import guess 5 | from fbuild.builders import find_program 6 | 7 | from fbuild.config.c import header_test, Test 8 | 9 | from fbuild.target import register 10 | from fbuild.record import Record 11 | from fbuild.path import Path 12 | import fbuild.db 13 | 14 | from functools import partial 15 | import os, re 16 | 17 | 18 | class Module(Record): pass 19 | 20 | 21 | def arguments(parser): 22 | group = parser.add_argument_group('config options') 23 | group.add_argument('--cc', help='Use the given C compiler') 24 | group.add_argument('--cflag', help='Pass the given flag to the C compiler', 25 | action='append', default=[]) 26 | group.add_argument('--use-color', help='Force C++ compiler colored output', 27 | action='store_true', default=True) 28 | group.add_argument('--release', help='Build in release mode', 29 | action='store_true', default=False) 30 | group.add_argument('--pkg-config', help='Use the given pkg-config executable') 31 | group.add_argument('--ruby', help='Use the given Ruby binary') 32 | group.add_argument('--mrkd', help='Use the given mrkd executable') 33 | group.add_argument('--destdir', help='Set the installation destdir', default='/') 34 | group.add_argument('--prefix', help='Set the installation prefix', default='usr') 35 | group.add_argument('--auto-service', 36 | help='Automatically stop services before installation', 37 | action='store_true', default=False) 38 | 39 | 40 | class Judy(Test): 41 | Judy_h = header_test('Judy.h') 42 | 43 | 44 | class MrkdBuilder(fbuild.db.PersistentObject): 45 | def __init__(self, ctx, exe=None): 46 | self.ctx = ctx 47 | self.mrkd = exe or find_program(ctx, ['mrkd']) 48 | 49 | @fbuild.db.cachemethod 50 | def convert(self, src: fbuild.db.SRC, *, index: fbuild.db.SRC, outdir, format): 51 | assert format in ('roff', 'html') 52 | 53 | src = Path(src) 54 | outdir = Path(outdir).addroot(self.ctx.buildroot) 55 | outdir.makedirs() 56 | 57 | ext = {'roff': '', 'html': '.html'}[format] 58 | dst = outdir / src.basename().replaceext(ext) 59 | 60 | cmd = [self.mrkd, src, dst, '-index', index, '-format', format] 61 | self.ctx.execute(cmd, 'mrkd', '%s -> %s' % (src, dst), color='yellow') 62 | self.ctx.db.add_external_dependencies_to_call(dsts=[dst]) 63 | return dst 64 | 65 | 66 | @fbuild.db.caches 67 | def ruby_version(ctx, ruby_bin): 68 | ctx.logger.check('checking %s version' % ruby_bin) 69 | out, _ = ctx.execute([ruby_bin, '--version'], quieter=1) 70 | m = re.match(r'ruby (\d\.\d)', out.decode('utf-8')) 71 | if m is None: 72 | ctx.logger.failed() 73 | return None 74 | ctx.logger.passed(m.group(1)) 75 | return m.group(1) 76 | 77 | 78 | @fbuild.db.caches 79 | def run_pkg_config(ctx, package): 80 | ctx.logger.check('checking for %s' % package) 81 | pkg = PkgConfig(ctx, package, exe=ctx.options.pkg_config) 82 | 83 | try: 84 | cflags = pkg.cflags() 85 | ldlibs = pkg.libs() 86 | except fbuild.ExecutionError: 87 | ctx.logger.failed() 88 | return None 89 | 90 | ctx.logger.passed(' '.join(cflags + ldlibs)) 91 | return Record(cflags=cflags, ldlibs=ldlibs) 92 | 93 | 94 | def print_config(ctx, rec): 95 | def padprint(c, msg): 96 | total = len(msg) + 22 97 | print(c * total) 98 | print(c * 10, msg, c * 10) 99 | print(c * total) 100 | 101 | def optprint(tag, value): 102 | print(tag, 'Yes.' if value else 'No.') 103 | 104 | print() 105 | padprint('*', 'Configure results') 106 | print() 107 | 108 | optprint('Release mode:', ctx.options.release) 109 | print('C compiler:', rec.c.static.compiler.cc.exe) 110 | print('C compiler flags:', ' '.join(set(rec.c.static.compiler.flags))) 111 | print('C linker flags:', ' '.join(set(rec.c.static.exe_linker.flags))) 112 | optprint('Build docs:', rec.mrkd) 113 | 114 | print() 115 | padprint('=', 'Modules') 116 | print() 117 | 118 | optprint('Python module:', rec.python3) 119 | optprint('Ruby module:', rec.ruby) 120 | print() 121 | 122 | 123 | @fbuild.db.caches 124 | def _configure(ctx, print_): 125 | platform = guess_platform(ctx) 126 | if 'linux' not in platform: 127 | raise fbuild.ConfigFailed('uprocd only runs under Linux.') 128 | 129 | flags = ctx.options.cflag 130 | posix_flags = ['-Wall', '-Werror', '-Wno-strict-prototypes', '-Wno-sign-compare'] 131 | clang_flags = [] 132 | 133 | if ctx.options.use_color: 134 | posix_flags.append('-fdiagnostics-color') 135 | if ctx.options.release: 136 | debug = False 137 | optimize = True 138 | else: 139 | debug = True 140 | optimize = False 141 | clang_flags.append('-fno-limit-debug-info') 142 | 143 | c = guess(ctx, exe=ctx.options.cc, flags=flags, 144 | debug=debug, optimize=optimize, platform_options=[ 145 | ({'clang'}, {'flags+': clang_flags}), 146 | ({'posix'}, {'flags+': posix_flags, 147 | 'external_libs+': ['dl']}), 148 | ]) 149 | 150 | libsystemd = run_pkg_config(ctx, 'libsystemd') 151 | if libsystemd is None: 152 | raise fbuild.ConfigFailed('libsystemd is required.') 153 | 154 | python3 = run_pkg_config(ctx, 'python3') 155 | 156 | ruby_bin = ruby = None 157 | try: 158 | ruby_bin = ctx.options.ruby or find_program(ctx, ['ruby']) 159 | except fbuild.ConfigFailed: 160 | pass 161 | else: 162 | ruby_ver = ruby_version(ctx, ruby_bin) 163 | if ruby_ver is not None: 164 | ruby = run_pkg_config(ctx, 'ruby-%s' % ruby_ver) 165 | 166 | try: 167 | mrkd = MrkdBuilder(ctx, ctx.options.mrkd) 168 | except fbuild.ConfigFailed: 169 | mrkd = None 170 | 171 | if not Judy(c.static).Judy_h: 172 | raise fbuild.ConfigFailed('Judy is required.') 173 | 174 | try: 175 | systemctl = find_program(ctx, ['systemctl']) 176 | except fbuild.ConfigFailed: 177 | systemctl = None 178 | 179 | rec = Record(c=c, libsystemd=libsystemd, python3=python3, ruby_bin=ruby_bin, 180 | ruby=ruby, mrkd=mrkd, systemctl=systemctl) 181 | if print_: 182 | print_config(ctx, rec) 183 | return rec 184 | 185 | 186 | @register() 187 | def configure(ctx): 188 | print('NOTE: If you want to reconfigure, run fbuild configure --rebuild instead.') 189 | rec = _configure(ctx, print_=False) 190 | print_config(ctx, rec) 191 | 192 | 193 | @fbuild.db.caches 194 | def symlink(ctx, src: fbuild.db.SRC, dst) -> fbuild.db.DST: 195 | dst = Path(dst).addroot(ctx.buildroot) 196 | ctx.logger.check(' * symlink', '%s -> %s' % (src, dst), color='yellow') 197 | 198 | if dst.lexists(): 199 | dst.remove() 200 | os.symlink(src.relpath(dst.parent), dst) 201 | 202 | return dst 203 | 204 | 205 | def build_module(ctx, module, *, rec, uprocctl): 206 | if 'pkg' in module: 207 | if module.pkg is None: 208 | return 209 | else: 210 | kw = {'cflags': module.pkg.cflags, 'ldlibs': module.pkg.ldlibs} 211 | else: 212 | kw = {} 213 | 214 | root = Path('modules') / module.name 215 | 216 | binaries = [] 217 | module_data = [] 218 | 219 | lib = rec.c.shared.build_lib(module.name, Path.glob('modules/%s/*.c' % module.name), 220 | includes=['api'], **kw) 221 | 222 | module_data.append(copy(ctx, lib, (root / module.name).replaceext('.so'))) 223 | for mod in [module.name] + module.others: 224 | path = (root / mod).replaceext('.module') 225 | module_data.append(copy(ctx, path, ctx.buildroot / path)) 226 | 227 | for file in module.files: 228 | path = root / file 229 | module_data.append(copy(ctx, path, ctx.buildroot / path)) 230 | 231 | for link in module.links: 232 | binaries.append(symlink(ctx, uprocctl, link)) 233 | 234 | return Record(binaries=binaries, data=module_data) 235 | 236 | 237 | def build_docs(ctx, src, *, rec, index): 238 | outdirs = {'roff': 'man', 'html': 'web'} 239 | man, html = ctx.scheduler.map(lambda fm: rec.mrkd.convert(src=src, index=index, 240 | outdir=outdirs[fm], format=fm), 241 | ['roff', 'html']) 242 | return Record(man=man, html=html) 243 | 244 | 245 | def build(ctx): 246 | rec = _configure(ctx, print_=True) 247 | ctx.install_destdir = ctx.options.destdir 248 | ctx.install_prefix = ctx.options.prefix 249 | 250 | sds = rec.c.static.build_lib('sds', ['sds/sds.c']) 251 | 252 | common_kw = dict( 253 | includes=['api', 'sds', 'src/common'], 254 | cflags=['-fvisibility=hidden'] + rec.libsystemd.cflags, 255 | ldlibs=['-Wl,--export-dynamic'] + rec.libsystemd.ldlibs, 256 | external_libs=['Judy'], 257 | libs=[sds], 258 | ) 259 | 260 | common = rec.c.static.build_lib('common', Path.glob('src/common/*.c'), **common_kw) 261 | 262 | # Avoid mutating the dict to ensure accurate rebuilds. 263 | common_kw = common_kw.copy() 264 | common_kw['libs'] = common_kw['libs'] + [common] 265 | 266 | cgrmvd = rec.c.static.build_exe('cgrmvd', Path.glob('src/cgrmvd/*.c'), **common_kw) 267 | uprocd = rec.c.static.build_exe('uprocd', Path.glob('src/uprocd/*.c'), **common_kw) 268 | uprocctl = rec.c.static.build_exe('uprocctl', Path.glob('src/uprocctl/*.c'), 269 | **common_kw) 270 | u = symlink(ctx, uprocctl, 'u') 271 | 272 | modules = [ 273 | Module(name='python', pkg=rec.python3, sources='python.c', 274 | others=['ipython', 'mrkd', 'mypy'], files=['_uprocd_modules.py'], 275 | links=['upython', 'uipython', 'umrkd', 'umypy']), 276 | Module(name='ruby', pkg=rec.ruby, sources='ruby.c', others=[], 277 | files=['_uprocd_requires.rb'], links=['uruby']), 278 | ] 279 | 280 | module_outputs = ctx.scheduler.map( 281 | partial(build_module, ctx, rec=rec, uprocctl=uprocctl), 282 | modules) 283 | 284 | if rec.mrkd is None: 285 | return 286 | 287 | u_man = copy(ctx, 'man/uprocctl.1.md', 'md/u.1.md') 288 | page_out = ctx.scheduler.map(partial(build_docs, ctx, rec=rec, 289 | index='man/index.ini'), 290 | [u_man] + Path.glob('man/*.md')) 291 | u_man_out = page_out[0] 292 | 293 | copy(ctx, 'web/index.html', 'web') 294 | 295 | ctx.install(cgrmvd, 'share/uprocd/bin') 296 | ctx.install(uprocd, 'share/uprocd/bin') 297 | ctx.install(uprocctl, 'bin') 298 | ctx.install(u, 'bin') 299 | 300 | ctx.install('misc/uprocd@.service', 'lib/systemd/user') 301 | ctx.install('misc/cgrmvd.service', 'lib/systemd/system') 302 | ctx.install('misc/uprocd.policy', 'share/cgrmvd/policies') 303 | ctx.install('misc/com.refi64.uprocd.Cgrmvd.conf', '/etc/dbus-1/system.d') 304 | 305 | for i, output in enumerate(module_outputs): 306 | if output is None: 307 | man = '%s.module' % modules[i].name 308 | page_out = [page for page in page_out \ 309 | if page.man.basename().replaceext('') != man] 310 | continue 311 | 312 | for bin in output.binaries: 313 | ctx.install(bin, 'bin') 314 | page_out.append(Record(man=u_man_out.man, rename='%s.1' % bin.basename())) 315 | for data in output.data: 316 | ctx.install(data, 'share/uprocd/modules') 317 | 318 | for page in page_out: 319 | rename = page.get('rename') 320 | section = page.man.ext.lstrip('.') 321 | 322 | ctx.install(page.man, 'share/man/man%s' % section, rename=rename) 323 | 324 | 325 | def pre_install(ctx): 326 | if ctx.options.auto_service: 327 | rec = _configure(ctx, print_=False) 328 | ctx.execute([rec.systemctl, 'stop', 'cgrmvd'], msg1='systemctl stop', 329 | msg2='cgrmvd', color='compile', ignore_error=True) 330 | ctx.execute([rec.systemctl, '--user', 'stop', 'uprocd.slice'], 331 | msg1='systemctl stop', msg2='uprocd (user)', color='compile', 332 | ignore_error=True) 333 | 334 | 335 | def post_install(ctx): 336 | if ctx.options.auto_service: 337 | rec = _configure(ctx, print_=False) 338 | ctx.execute([rec.systemctl, 'daemon-reload'], msg1='systemctl', 339 | msg2='daemon-reload', color='compile', ignore_error=True) 340 | ctx.execute([rec.systemctl, '--user', 'daemon-reload'], msg1='systemctl', 341 | msg2='daemon-reload (user)', color='compile', ignore_error=True) 342 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /modules/python/_uprocd_modules.py: -------------------------------------------------------------------------------- 1 | 2 | try: 3 | import string 4 | except Exception as ex: 5 | print('string', ex) 6 | 7 | try: 8 | import re 9 | except Exception as ex: 10 | print('re', ex) 11 | 12 | try: 13 | import difflib 14 | except Exception as ex: 15 | print('difflib', ex) 16 | 17 | try: 18 | import textwrap 19 | except Exception as ex: 20 | print('textwrap', ex) 21 | 22 | try: 23 | import unicodedata 24 | except Exception as ex: 25 | print('unicodedata', ex) 26 | 27 | try: 28 | import stringprep 29 | except Exception as ex: 30 | print('stringprep', ex) 31 | 32 | try: 33 | import readline 34 | except Exception as ex: 35 | print('readline', ex) 36 | 37 | try: 38 | import rlcompleter 39 | except Exception as ex: 40 | print('rlcompleter', ex) 41 | 42 | try: 43 | import struct 44 | except Exception as ex: 45 | print('struct', ex) 46 | 47 | try: 48 | import codecs 49 | except Exception as ex: 50 | print('codecs', ex) 51 | 52 | try: 53 | import datetime 54 | except Exception as ex: 55 | print('datetime', ex) 56 | 57 | try: 58 | import calendar 59 | except Exception as ex: 60 | print('calendar', ex) 61 | 62 | try: 63 | import collections 64 | except Exception as ex: 65 | print('collections', ex) 66 | 67 | try: 68 | import heapq 69 | except Exception as ex: 70 | print('heapq', ex) 71 | 72 | try: 73 | import bisect 74 | except Exception as ex: 75 | print('bisect', ex) 76 | 77 | try: 78 | import array 79 | except Exception as ex: 80 | print('array', ex) 81 | 82 | try: 83 | import weakref 84 | except Exception as ex: 85 | print('weakref', ex) 86 | 87 | try: 88 | import types 89 | except Exception as ex: 90 | print('types', ex) 91 | 92 | try: 93 | import copy 94 | except Exception as ex: 95 | print('copy', ex) 96 | 97 | try: 98 | import pprint 99 | except Exception as ex: 100 | print('pprint', ex) 101 | 102 | try: 103 | import reprlib 104 | except Exception as ex: 105 | print('reprlib', ex) 106 | 107 | try: 108 | import enum 109 | except Exception as ex: 110 | print('enum', ex) 111 | 112 | try: 113 | import numbers 114 | except Exception as ex: 115 | print('numbers', ex) 116 | 117 | try: 118 | import math 119 | except Exception as ex: 120 | print('math', ex) 121 | 122 | try: 123 | import cmath 124 | except Exception as ex: 125 | print('cmath', ex) 126 | 127 | try: 128 | import decimal 129 | except Exception as ex: 130 | print('decimal', ex) 131 | 132 | try: 133 | import fractions 134 | except Exception as ex: 135 | print('fractions', ex) 136 | 137 | try: 138 | import random 139 | except Exception as ex: 140 | print('random', ex) 141 | 142 | try: 143 | import statistics 144 | except Exception as ex: 145 | print('statistics', ex) 146 | 147 | try: 148 | import itertools 149 | except Exception as ex: 150 | print('itertools', ex) 151 | 152 | try: 153 | import functools 154 | except Exception as ex: 155 | print('functools', ex) 156 | 157 | try: 158 | import operator 159 | except Exception as ex: 160 | print('operator', ex) 161 | 162 | try: 163 | import pathlib 164 | except Exception as ex: 165 | print('pathlib', ex) 166 | 167 | try: 168 | import fileinput 169 | except Exception as ex: 170 | print('fileinput', ex) 171 | 172 | try: 173 | import stat 174 | except Exception as ex: 175 | print('stat', ex) 176 | 177 | try: 178 | import filecmp 179 | except Exception as ex: 180 | print('filecmp', ex) 181 | 182 | try: 183 | import tempfile 184 | except Exception as ex: 185 | print('tempfile', ex) 186 | 187 | try: 188 | import glob 189 | except Exception as ex: 190 | print('glob', ex) 191 | 192 | try: 193 | import fnmatch 194 | except Exception as ex: 195 | print('fnmatch', ex) 196 | 197 | try: 198 | import linecache 199 | except Exception as ex: 200 | print('linecache', ex) 201 | 202 | try: 203 | import shutil 204 | except Exception as ex: 205 | print('shutil', ex) 206 | 207 | try: 208 | import macpath 209 | except Exception as ex: 210 | print('macpath', ex) 211 | 212 | try: 213 | import pickle 214 | except Exception as ex: 215 | print('pickle', ex) 216 | 217 | try: 218 | import copyreg 219 | except Exception as ex: 220 | print('copyreg', ex) 221 | 222 | try: 223 | import pickle 224 | except Exception as ex: 225 | print('pickle', ex) 226 | 227 | try: 228 | import shelve 229 | except Exception as ex: 230 | print('shelve', ex) 231 | 232 | try: 233 | import marshal 234 | except Exception as ex: 235 | print('marshal', ex) 236 | 237 | try: 238 | import dbm 239 | except Exception as ex: 240 | print('dbm', ex) 241 | 242 | try: 243 | import sqlite3 244 | except Exception as ex: 245 | print('sqlite3', ex) 246 | 247 | try: 248 | import zlib 249 | except Exception as ex: 250 | print('zlib', ex) 251 | 252 | try: 253 | import gzip 254 | except Exception as ex: 255 | print('gzip', ex) 256 | 257 | try: 258 | import bz2 259 | except Exception as ex: 260 | print('bz2', ex) 261 | 262 | try: 263 | import lzma 264 | except Exception as ex: 265 | print('lzma', ex) 266 | 267 | try: 268 | import zipfile 269 | except Exception as ex: 270 | print('zipfile', ex) 271 | 272 | try: 273 | import tarfile 274 | except Exception as ex: 275 | print('tarfile', ex) 276 | 277 | try: 278 | import csv 279 | except Exception as ex: 280 | print('csv', ex) 281 | 282 | try: 283 | import configparser 284 | except Exception as ex: 285 | print('configparser', ex) 286 | 287 | try: 288 | import netrc 289 | except Exception as ex: 290 | print('netrc', ex) 291 | 292 | try: 293 | import xdrlib 294 | except Exception as ex: 295 | print('xdrlib', ex) 296 | 297 | try: 298 | import plistlib 299 | except Exception as ex: 300 | print('plistlib', ex) 301 | 302 | try: 303 | import hashlib 304 | except Exception as ex: 305 | print('hashlib', ex) 306 | 307 | try: 308 | import hmac 309 | except Exception as ex: 310 | print('hmac', ex) 311 | 312 | try: 313 | import secrets 314 | except Exception as ex: 315 | print('secrets', ex) 316 | 317 | try: 318 | import os 319 | except Exception as ex: 320 | print('os', ex) 321 | 322 | try: 323 | import io 324 | except Exception as ex: 325 | print('io', ex) 326 | 327 | try: 328 | import time 329 | except Exception as ex: 330 | print('time', ex) 331 | 332 | try: 333 | import argparse 334 | except Exception as ex: 335 | print('argparse', ex) 336 | 337 | try: 338 | import getopt 339 | except Exception as ex: 340 | print('getopt', ex) 341 | 342 | try: 343 | import logging 344 | except Exception as ex: 345 | print('logging', ex) 346 | 347 | try: 348 | import getpass 349 | except Exception as ex: 350 | print('getpass', ex) 351 | 352 | try: 353 | import curses 354 | except Exception as ex: 355 | print('curses', ex) 356 | 357 | try: 358 | import platform 359 | except Exception as ex: 360 | print('platform', ex) 361 | 362 | try: 363 | import errno 364 | except Exception as ex: 365 | print('errno', ex) 366 | 367 | try: 368 | import ctypes 369 | except Exception as ex: 370 | print('ctypes', ex) 371 | 372 | try: 373 | import threading 374 | except Exception as ex: 375 | print('threading', ex) 376 | 377 | try: 378 | import multiprocessing 379 | except Exception as ex: 380 | print('multiprocessing', ex) 381 | 382 | try: 383 | import concurrent 384 | except Exception as ex: 385 | print('concurrent', ex) 386 | 387 | try: 388 | import subprocess 389 | except Exception as ex: 390 | print('subprocess', ex) 391 | 392 | try: 393 | import sched 394 | except Exception as ex: 395 | print('sched', ex) 396 | 397 | try: 398 | import queue 399 | except Exception as ex: 400 | print('queue', ex) 401 | 402 | try: 403 | import dummy_threading 404 | except Exception as ex: 405 | print('dummy_threading', ex) 406 | 407 | try: 408 | import threading 409 | except Exception as ex: 410 | print('threading', ex) 411 | 412 | try: 413 | import socket 414 | except Exception as ex: 415 | print('socket', ex) 416 | 417 | try: 418 | import ssl 419 | except Exception as ex: 420 | print('ssl', ex) 421 | 422 | try: 423 | import select 424 | except Exception as ex: 425 | print('select', ex) 426 | 427 | try: 428 | import selectors 429 | except Exception as ex: 430 | print('selectors', ex) 431 | 432 | try: 433 | import asyncio 434 | except Exception as ex: 435 | print('asyncio', ex) 436 | 437 | try: 438 | import asyncore 439 | except Exception as ex: 440 | print('asyncore', ex) 441 | 442 | try: 443 | import asynchat 444 | except Exception as ex: 445 | print('asynchat', ex) 446 | 447 | try: 448 | import signal 449 | except Exception as ex: 450 | print('signal', ex) 451 | 452 | try: 453 | import mmap 454 | except Exception as ex: 455 | print('mmap', ex) 456 | 457 | try: 458 | import email 459 | except Exception as ex: 460 | print('email', ex) 461 | 462 | try: 463 | import json 464 | except Exception as ex: 465 | print('json', ex) 466 | 467 | try: 468 | import mailcap 469 | except Exception as ex: 470 | print('mailcap', ex) 471 | 472 | try: 473 | import mailbox 474 | except Exception as ex: 475 | print('mailbox', ex) 476 | 477 | try: 478 | import mimetypes 479 | except Exception as ex: 480 | print('mimetypes', ex) 481 | 482 | try: 483 | import base64 484 | except Exception as ex: 485 | print('base64', ex) 486 | 487 | try: 488 | import binhex 489 | except Exception as ex: 490 | print('binhex', ex) 491 | 492 | try: 493 | import binascii 494 | except Exception as ex: 495 | print('binascii', ex) 496 | 497 | try: 498 | import quopri 499 | except Exception as ex: 500 | print('quopri', ex) 501 | 502 | try: 503 | import uu 504 | except Exception as ex: 505 | print('uu', ex) 506 | 507 | try: 508 | import html 509 | except Exception as ex: 510 | print('html', ex) 511 | 512 | try: 513 | import webbrowser 514 | except Exception as ex: 515 | print('webbrowser', ex) 516 | 517 | try: 518 | import cgi 519 | except Exception as ex: 520 | print('cgi', ex) 521 | 522 | try: 523 | import cgitb 524 | except Exception as ex: 525 | print('cgitb', ex) 526 | 527 | try: 528 | import wsgiref 529 | except Exception as ex: 530 | print('wsgiref', ex) 531 | 532 | try: 533 | import urllib 534 | except Exception as ex: 535 | print('urllib', ex) 536 | 537 | try: 538 | import http 539 | except Exception as ex: 540 | print('http', ex) 541 | 542 | try: 543 | import ftplib 544 | except Exception as ex: 545 | print('ftplib', ex) 546 | 547 | try: 548 | import poplib 549 | except Exception as ex: 550 | print('poplib', ex) 551 | 552 | try: 553 | import imaplib 554 | except Exception as ex: 555 | print('imaplib', ex) 556 | 557 | try: 558 | import nntplib 559 | except Exception as ex: 560 | print('nntplib', ex) 561 | 562 | try: 563 | import smtplib 564 | except Exception as ex: 565 | print('smtplib', ex) 566 | 567 | try: 568 | import smtpd 569 | except Exception as ex: 570 | print('smtpd', ex) 571 | 572 | try: 573 | import telnetlib 574 | except Exception as ex: 575 | print('telnetlib', ex) 576 | 577 | try: 578 | import uuid 579 | except Exception as ex: 580 | print('uuid', ex) 581 | 582 | try: 583 | import socketserver 584 | except Exception as ex: 585 | print('socketserver', ex) 586 | 587 | try: 588 | import xmlrpc 589 | except Exception as ex: 590 | print('xmlrpc', ex) 591 | 592 | try: 593 | import ipaddress 594 | except Exception as ex: 595 | print('ipaddress', ex) 596 | 597 | try: 598 | import audioop 599 | except Exception as ex: 600 | print('audioop', ex) 601 | 602 | try: 603 | import aifc 604 | except Exception as ex: 605 | print('aifc', ex) 606 | 607 | try: 608 | import sunau 609 | except Exception as ex: 610 | print('sunau', ex) 611 | 612 | try: 613 | import wave 614 | except Exception as ex: 615 | print('wave', ex) 616 | 617 | try: 618 | import chunk 619 | except Exception as ex: 620 | print('chunk', ex) 621 | 622 | try: 623 | import colorsys 624 | except Exception as ex: 625 | print('colorsys', ex) 626 | 627 | try: 628 | import imghdr 629 | except Exception as ex: 630 | print('imghdr', ex) 631 | 632 | try: 633 | import sndhdr 634 | except Exception as ex: 635 | print('sndhdr', ex) 636 | 637 | try: 638 | import ossaudiodev 639 | except Exception as ex: 640 | print('ossaudiodev', ex) 641 | 642 | try: 643 | import gettext 644 | except Exception as ex: 645 | print('gettext', ex) 646 | 647 | try: 648 | import locale 649 | except Exception as ex: 650 | print('locale', ex) 651 | 652 | try: 653 | import turtle 654 | except Exception as ex: 655 | print('turtle', ex) 656 | 657 | try: 658 | import cmd 659 | except Exception as ex: 660 | print('cmd', ex) 661 | 662 | try: 663 | import shlex 664 | except Exception as ex: 665 | print('shlex', ex) 666 | 667 | try: 668 | import tkinter 669 | except Exception as ex: 670 | print('tkinter', ex) 671 | 672 | try: 673 | import typing 674 | except Exception as ex: 675 | print('typing', ex) 676 | 677 | try: 678 | import pydoc 679 | except Exception as ex: 680 | print('pydoc', ex) 681 | 682 | try: 683 | import doctest 684 | except Exception as ex: 685 | print('doctest', ex) 686 | 687 | try: 688 | import unittest 689 | except Exception as ex: 690 | print('unittest', ex) 691 | 692 | try: 693 | import test 694 | except Exception as ex: 695 | print('test', ex) 696 | 697 | try: 698 | import bdb 699 | except Exception as ex: 700 | print('bdb', ex) 701 | 702 | try: 703 | import faulthandler 704 | except Exception as ex: 705 | print('faulthandler', ex) 706 | 707 | try: 708 | import pdb 709 | except Exception as ex: 710 | print('pdb', ex) 711 | 712 | try: 713 | import timeit 714 | except Exception as ex: 715 | print('timeit', ex) 716 | 717 | try: 718 | import trace 719 | except Exception as ex: 720 | print('trace', ex) 721 | 722 | try: 723 | import tracemalloc 724 | except Exception as ex: 725 | print('tracemalloc', ex) 726 | 727 | try: 728 | import distutils 729 | except Exception as ex: 730 | print('distutils', ex) 731 | 732 | try: 733 | import ensurepip 734 | except Exception as ex: 735 | print('ensurepip', ex) 736 | 737 | try: 738 | import pip 739 | except Exception as ex: 740 | print('pip', ex) 741 | 742 | try: 743 | import venv 744 | except Exception as ex: 745 | print('venv', ex) 746 | 747 | try: 748 | import zipapp 749 | except Exception as ex: 750 | print('zipapp', ex) 751 | 752 | try: 753 | import sys 754 | except Exception as ex: 755 | print('sys', ex) 756 | 757 | try: 758 | import sysconfig 759 | except Exception as ex: 760 | print('sysconfig', ex) 761 | 762 | try: 763 | import builtins 764 | except Exception as ex: 765 | print('builtins', ex) 766 | 767 | try: 768 | import warnings 769 | except Exception as ex: 770 | print('warnings', ex) 771 | 772 | try: 773 | import contextlib 774 | except Exception as ex: 775 | print('contextlib', ex) 776 | 777 | try: 778 | import abc 779 | except Exception as ex: 780 | print('abc', ex) 781 | 782 | try: 783 | import atexit 784 | except Exception as ex: 785 | print('atexit', ex) 786 | 787 | try: 788 | import traceback 789 | except Exception as ex: 790 | print('traceback', ex) 791 | 792 | try: 793 | import gc 794 | except Exception as ex: 795 | print('gc', ex) 796 | 797 | try: 798 | import inspect 799 | except Exception as ex: 800 | print('inspect', ex) 801 | 802 | try: 803 | import site 804 | except Exception as ex: 805 | print('site', ex) 806 | 807 | try: 808 | import fpectl 809 | except Exception as ex: 810 | print('fpectl', ex) 811 | 812 | try: 813 | import code 814 | except Exception as ex: 815 | print('code', ex) 816 | 817 | try: 818 | import codeop 819 | except Exception as ex: 820 | print('codeop', ex) 821 | 822 | try: 823 | import zipimport 824 | except Exception as ex: 825 | print('zipimport', ex) 826 | 827 | try: 828 | import pkgutil 829 | except Exception as ex: 830 | print('pkgutil', ex) 831 | 832 | try: 833 | import modulefinder 834 | except Exception as ex: 835 | print('modulefinder', ex) 836 | 837 | try: 838 | import runpy 839 | except Exception as ex: 840 | print('runpy', ex) 841 | 842 | try: 843 | import importlib 844 | except Exception as ex: 845 | print('importlib', ex) 846 | 847 | try: 848 | import parser 849 | except Exception as ex: 850 | print('parser', ex) 851 | 852 | try: 853 | import ast 854 | except Exception as ex: 855 | print('ast', ex) 856 | 857 | try: 858 | import symtable 859 | except Exception as ex: 860 | print('symtable', ex) 861 | 862 | try: 863 | import symbol 864 | except Exception as ex: 865 | print('symbol', ex) 866 | 867 | try: 868 | import token 869 | except Exception as ex: 870 | print('token', ex) 871 | 872 | try: 873 | import keyword 874 | except Exception as ex: 875 | print('keyword', ex) 876 | 877 | try: 878 | import tokenize 879 | except Exception as ex: 880 | print('tokenize', ex) 881 | 882 | try: 883 | import tabnanny 884 | except Exception as ex: 885 | print('tabnanny', ex) 886 | 887 | try: 888 | import pyclbr 889 | except Exception as ex: 890 | print('pyclbr', ex) 891 | 892 | try: 893 | import py_compile 894 | except Exception as ex: 895 | print('py_compile', ex) 896 | 897 | try: 898 | import compileall 899 | except Exception as ex: 900 | print('compileall', ex) 901 | 902 | try: 903 | import dis 904 | except Exception as ex: 905 | print('dis', ex) 906 | 907 | try: 908 | import pickletools 909 | except Exception as ex: 910 | print('pickletools', ex) 911 | 912 | try: 913 | import formatter 914 | except Exception as ex: 915 | print('formatter', ex) 916 | 917 | try: 918 | import msilib 919 | except Exception as ex: 920 | print('msilib', ex) 921 | 922 | try: 923 | import msvcrt 924 | except Exception as ex: 925 | print('msvcrt', ex) 926 | 927 | try: 928 | import winreg 929 | except Exception as ex: 930 | print('winreg', ex) 931 | 932 | try: 933 | import winsound 934 | except Exception as ex: 935 | print('winsound', ex) 936 | 937 | try: 938 | import posix 939 | except Exception as ex: 940 | print('posix', ex) 941 | 942 | try: 943 | import pwd 944 | except Exception as ex: 945 | print('pwd', ex) 946 | 947 | try: 948 | import spwd 949 | except Exception as ex: 950 | print('spwd', ex) 951 | 952 | try: 953 | import grp 954 | except Exception as ex: 955 | print('grp', ex) 956 | 957 | try: 958 | import crypt 959 | except Exception as ex: 960 | print('crypt', ex) 961 | 962 | try: 963 | import termios 964 | except Exception as ex: 965 | print('termios', ex) 966 | 967 | try: 968 | import tty 969 | except Exception as ex: 970 | print('tty', ex) 971 | 972 | try: 973 | import pty 974 | except Exception as ex: 975 | print('pty', ex) 976 | 977 | try: 978 | import fcntl 979 | except Exception as ex: 980 | print('fcntl', ex) 981 | 982 | try: 983 | import fcntl 984 | except Exception as ex: 985 | print('fcntl', ex) 986 | 987 | try: 988 | import ioctl 989 | except Exception as ex: 990 | print('ioctl', ex) 991 | 992 | try: 993 | import pipes 994 | except Exception as ex: 995 | print('pipes', ex) 996 | 997 | try: 998 | import resource 999 | except Exception as ex: 1000 | print('resource', ex) 1001 | 1002 | try: 1003 | import nis 1004 | except Exception as ex: 1005 | print('nis', ex) 1006 | 1007 | try: 1008 | import syslog 1009 | except Exception as ex: 1010 | print('syslog', ex) 1011 | 1012 | try: 1013 | import optparse 1014 | except Exception as ex: 1015 | print('optparse', ex) 1016 | 1017 | try: 1018 | import imp 1019 | except Exception as ex: 1020 | print('imp', ex) 1021 | --------------------------------------------------------------------------------