├── .gitignore ├── utils ├── autostrace │ ├── todo.txt │ ├── README.md │ └── autostrace.py ├── jaraudit │ ├── README.md │ └── jaraudit.py ├── gitslurp │ ├── README.md │ └── gitslurp.py ├── dimmer │ ├── README.md │ └── dimmer.ahk ├── retina │ ├── README.md │ └── retina.m ├── permute │ ├── README.md │ └── permute.py ├── beamsync │ ├── README.md │ ├── self.beamsync.disable.plist │ └── beamsync.m ├── gateway │ └── gateway ├── pidrun │ └── pidrun ├── aotool │ ├── README.md │ └── aotool.c ├── sonosd │ ├── README.md │ └── sonosd.py ├── brew_altuser │ ├── README.md │ └── install_brew_altuser.sh ├── repl │ └── repl ├── hardlink │ └── hardlink.sh ├── binsplit │ └── binsplit ├── tvsort │ ├── README.md │ └── tvsort.py ├── dumpmem │ └── dumpmem.c ├── revmatch │ └── revmatch.py ├── vm │ └── vm.sh ├── taplist │ ├── README.md │ └── taplist.c ├── shotsum │ └── shotsum ├── macbinary │ └── macbinary.py ├── shotgun │ └── shotgun ├── arctop │ └── arctop.py └── snakedisk │ └── snakedisk.py ├── snippets └── python │ ├── singleton.py │ ├── tailcall.py │ ├── netreg.py │ └── threadpool.py ├── LICENSE ├── README.md └── scripts ├── install_brew_altuser.sh └── install_port_altuser.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /utils/autostrace/todo.txt: -------------------------------------------------------------------------------- 1 | add mac/dtruss support 2 | -------------------------------------------------------------------------------- /utils/autostrace/README.md: -------------------------------------------------------------------------------- 1 | autostrace.py 2 | ========== 3 | automatically runs strace and logs the output for all processes of a certain name started by a specified user 4 | 5 | -------------------------------------------------------------------------------- /utils/jaraudit/README.md: -------------------------------------------------------------------------------- 1 | jaraudit 2 | -------- 3 | 4 | Checks your Java dependencies for CVEs by scanning for pom.xml and .jar files. 5 | 6 | Usage: jaraudit.py [-j,--json] path [path...] 7 | -------------------------------------------------------------------------------- /utils/gitslurp/README.md: -------------------------------------------------------------------------------- 1 | gitslurp 2 | ==== 3 | 4 | Batch clone all repositories from a GitHub organization. Prompts for an access token on stdin: https://github.com/settings/tokens 5 | 6 | Usage: gitslurp.py [ssh key path] 7 | -------------------------------------------------------------------------------- /utils/dimmer/README.md: -------------------------------------------------------------------------------- 1 | Dimmer.ahk 2 | ========== 3 | Allows you to dim/undim a monitor using a hotkey. Requires AutoHotkey_L. 4 | Press your configured modifier + a number key to affect a monitor. 5 | 6 | The modifier can be configured at the top of the script. 7 | -------------------------------------------------------------------------------- /utils/retina/README.md: -------------------------------------------------------------------------------- 1 | retina 2 | ==== 3 | 4 | Set unscaled resolution modes on a Retina MacBook. 5 | 6 | Building 7 | ---- 8 | 9 | sh retina.m 10 | 11 | Usage 12 | ---- 13 | 14 | - Example: `./retina 2560x1600`. 15 | - Run `./retina` to list available modes. 16 | -------------------------------------------------------------------------------- /utils/permute/README.md: -------------------------------------------------------------------------------- 1 | permute.py 2 | ========== 3 | creates permutations of text based on chains 4 | 5 | intended to find combinations of a word, taking into account upper+lowercase and similar-looking characters like 'i', '1', 'l', '!' 6 | 7 | accepts multiple words via either newline-separated stdin and regular command-line arguments 8 | -------------------------------------------------------------------------------- /utils/beamsync/README.md: -------------------------------------------------------------------------------- 1 | beamsync 2 | ==== 3 | 4 | Disable BeamSync on OS X. 5 | 6 | Building 7 | ---- 8 | 9 | sh beamsync.m 10 | 11 | Running 12 | ---- 13 | 14 | ./beamsync 15 | 16 | 17 | Running at Login 18 | ---- 19 | 20 | 1. Update Program in `self.beamsync.disable.plist` to point to the compiled `beamsync` binary. 21 | 2. Place `self.beamsync.disable.plist` inside `~/Library/LaunchAgents`. 22 | -------------------------------------------------------------------------------- /utils/gateway/gateway: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | join <(t=`echo -e '\t'`; ifconfig | egrep -A4 'en[0-9].*:|eth[0-9].*' | egrep -o 'en[0-9][^:]*:|eth[0-9][^ \t]*[ \t]|wlan[0-9][^ \t]*[ \t]|inet (addr:)?([0-9]+\.?){4}' | sed -e 's/addr://' | tr '\n' '\t' | sed -Ee "s/${t}inet//g" -e "s/${t}\$//" | tr '\t' '\n' | sort) <(netstat -nr | egrep 'default|U.*G' | awk '{print $8, $6 ":", "gateway", $2}' | sed -Ee 's/(eth[^ \t]+|wlan[^ \t]+)([^:]+):/\1:/' -e 's/^ +//' | sort) 3 | 4 | -------------------------------------------------------------------------------- /utils/pidrun/pidrun: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | # run an executable with a specified PID 3 | 4 | if [[ $# -ne 2 ]]; then 5 | echo "Usage: pidrun " 6 | exit 1 7 | fi 8 | 9 | pid="$1" 10 | target="$2" 11 | export target 12 | 13 | # paired with our kill -SIGHUP, exit the loop when the target program finishes 14 | trap exit SIGHUP 15 | 16 | while [[ 1 ]]; do 17 | bash -c '[[ $$ -eq '"$pid"' ]] && { kill -SIGHUP "$PPID"; exec "$target"; }' 18 | done 19 | -------------------------------------------------------------------------------- /utils/beamsync/self.beamsync.disable.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | self.beamsync.disable 7 | Program 8 | /path/to/beamsync 9 | RunAtLoad 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /utils/aotool/README.md: -------------------------------------------------------------------------------- 1 | aotool 2 | ==== 3 | 4 | Create and dump Rosetta 2 .aot assemblies (without using root or disabling SIP). 5 | 6 | Building 7 | ---- 8 | 9 | sh aotool.c 10 | 11 | Usage 12 | ---- 13 | 14 | ``` 15 | Usage: aotool -l # Dump an .aot for an existing x86_64 binary 16 | aotool -c # Compile a C file and dump the resulting .aot 17 | aotool -d # Compile a C file and show both x86_64 and .aot arm64 disassembly. 18 | ``` 19 | -------------------------------------------------------------------------------- /utils/sonosd/README.md: -------------------------------------------------------------------------------- 1 | sonosd 2 | ------ 3 | 4 | Redis-backed Sonos control daemon, with smooth volume control. 5 | 6 | This depends on a running Redis server, and assumes your primary Sonos is called "Master". 7 | 8 | Commands 9 | ----- 10 | 11 | - vol\_up 12 | - vol\_down 13 | - play (acts as play/pause) 14 | - next 15 | - prev 16 | 17 | 18 | Usage 19 | ----- 20 | 21 | From a command line: 22 | 23 | redis-cli publish sonos vol_up 24 | 25 | From AppleScript (for the Griffin PowerMate control knob): 26 | 27 | do shell script "/usr/local/bin/redis-cli publish sonos vol_up" 28 | -------------------------------------------------------------------------------- /utils/brew_altuser/README.md: -------------------------------------------------------------------------------- 1 | brew_altuser 2 | ==== 3 | 4 | Installs Homebrew on macOS to a new user named "brew", sets up /opt/sudobrew as a passwordless sudo command to run the `brew` command as the "brew" user. 5 | 6 | To use, run `./install_brew_altuser.sh` 7 | 8 | ### Extras 9 | 10 | Run `chmod 750 "$HOME"` if you want to prevent homebrew builds from accessing your home directory. 11 | 12 | If your shell isn't `zsh` or `bash`, you'll want to port this alias to your shell profile `brew() { sudo /opt/sudobrew "$@"; }` so you can run the `brew` command. 13 | 14 | Casks that install apps to /Applications don't work, and will never work, as the `brew` user does not and should not have `sudo`. Figure something else out. 15 | -------------------------------------------------------------------------------- /snippets/python/singleton.py: -------------------------------------------------------------------------------- 1 | class Singleton(type): 2 | __singleton = None 3 | 4 | def __new__(cls, names, bases, attrs): 5 | cls = super(Singleton, cls).__new__(cls, names, bases, attrs) 6 | cls.__singleton = None 7 | return cls 8 | 9 | def __call__(cls, *args, **kwargs): 10 | if cls.__singleton is not None: 11 | return cls.__singleton 12 | else: 13 | instance = super(Singleton, cls).__call__(*args, **kwargs) 14 | cls.__singleton = instance 15 | return instance 16 | 17 | class Test: 18 | __metaclass__ = Singleton 19 | 20 | def __init__(self, args): 21 | print 'Test(%s)' % args 22 | 23 | if __name__ == '__main__': 24 | a = Test(1) 25 | b = Test(2) 26 | print a is b 27 | -------------------------------------------------------------------------------- /utils/autostrace/autostrace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os, time, sys 3 | mypid = str(os.getpid()) 4 | 5 | if len(sys.argv) < 3: 6 | print 'Usage: ./autostrace.py [username] [process] (extra strace args)' 7 | sys.exit(1) 8 | 9 | username = sys.argv[1] 10 | process = sys.argv[2] 11 | 12 | args = '' 13 | if len(sys.argv) > 3: 14 | args = ' ' + ' '.join(sys.argv[2:]) 15 | 16 | already = [mypid] 17 | while True: 18 | ps = os.popen('\ps -u "%s" -o pid,command | tail -n+2 | grep %s' % (username, process)).read() 19 | for line in ps.strip().split('\n'): 20 | if not line.strip(): continue 21 | pid, command = line.split(None, 1) 22 | if pid in already: continue 23 | print 'watching %s' % pid 24 | already.append(pid) 25 | os.system(('strace %s -p %s -o strace_%s.log &' % (args, pid, pid))) 26 | 27 | time.sleep(0.05) 28 | -------------------------------------------------------------------------------- /utils/gitslurp/gitslurp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pipes 3 | import requests 4 | import subprocess 5 | import sys 6 | 7 | def clone(url, key=None): 8 | if key: 9 | p = subprocess.Popen(['ssh-agent', 'bash', '-c', 'ssh-add ' + pipes.quote(key) + ' &>/dev/null; git clone ' + pipes.quote(url)]) 10 | else: 11 | p = subprocess.Popen(['git', 'clone', url]) 12 | p.communicate() 13 | 14 | def clone_all(org, username, token, key=None): 15 | url = 'https://api.github.com/orgs/{}/repos'.format(org) 16 | r = requests.get(url, auth=(username, token)) 17 | for repo in r.json(): 18 | clone(repo['ssh_url'], key) 19 | print 20 | 21 | if __name__ == '__main__': 22 | if len(sys.argv) < 3: 23 | print 'Usage: %s [ssh key path]' % sys.argv[0] 24 | sys.exit(1) 25 | 26 | org = sys.argv[1] 27 | user = sys.argv[2] 28 | key = None 29 | if len(sys.argv) >= 4: 30 | key = sys.argv[3] 31 | 32 | token = raw_input('Access token: ') 33 | clone_all(org, user, token, key) 34 | -------------------------------------------------------------------------------- /utils/repl/repl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import subprocess 4 | import shlex 5 | import cmd 6 | import sys 7 | 8 | class Repl(cmd.Cmd): 9 | def default(self, line): 10 | if line == 'EOF': 11 | return 12 | 13 | if self.one: 14 | args = [line] 15 | else: 16 | args = shlex.split(line) 17 | 18 | self.run(self.prefix + args) 19 | 20 | def postcmd(self, stop, line): 21 | if line == 'EOF': 22 | print 23 | return True 24 | 25 | def run(self, cmd): 26 | subprocess.call(cmd) 27 | 28 | def repl(prefix, one=False): 29 | r = Repl() 30 | r.prefix = prefix 31 | r.prompt = '$ ' 32 | r.one = one 33 | r.cmdloop() 34 | 35 | if __name__ == '__main__': 36 | if len(sys.argv) < 2: 37 | print 'Usage: repl [arg1] ...' 38 | print 39 | print ' -1 quote additional input to command' 40 | sys.exit(1) 41 | 42 | one = False 43 | if sys.argv[1] == '-1': 44 | sys.argv.pop(1) 45 | one = True 46 | 47 | repl(sys.argv[1:], one) 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Ryan Hileman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /utils/beamsync/beamsync.m: -------------------------------------------------------------------------------- 1 | #/* 2 | echo "Building ./beamsync" 3 | gcc "$0" -framework Foundation -framework AppKit -o beamsync 4 | exit 0 5 | */ 6 | #import 7 | #include 8 | 9 | extern void CGSSetDebugOptions(int); 10 | extern void CGSDeferredUpdates(int); 11 | 12 | enum { 13 | disable = 0, 14 | automatic = 1, 15 | forced = 2 16 | }; 17 | 18 | void set(int mode) { 19 | CGSSetDebugOptions(mode ? 0 : 0x08000000); 20 | CGSDeferredUpdates(mode); 21 | } 22 | 23 | int try(const char *cmd, const char *match, int flag) { 24 | if (strcmp(cmd, match) == 0) { 25 | set(flag); 26 | NSLog(@"BeamSync %sd.", match); 27 | return 1; 28 | } 29 | return 0; 30 | } 31 | 32 | int main(int argc, const char *argv[]) { 33 | if (argc > 1) { 34 | if (!(try(argv[1], "enable", automatic) || 35 | try(argv[1], "disable", disable))) { 36 | printf("Usage: %s [enable|disable]\n", argv[0]); 37 | return 1; 38 | } 39 | } else { 40 | set(disable); 41 | NSLog(@"BeamSync disabled."); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /snippets/python/tailcall.py: -------------------------------------------------------------------------------- 1 | import thread 2 | 3 | def tail(func): 4 | stacks = {} 5 | 6 | def wrapper(*args, **kwargs): 7 | tid = thread.get_ident() 8 | topmost = tid not in stacks 9 | if topmost: 10 | count = 1 11 | stack = stacks[tid] = [] 12 | try: 13 | value = func(*args, **kwargs) 14 | while len(stack) > 0: 15 | count += 1 16 | args, kwargs = stack.pop() 17 | value = func(*args, **kwargs) 18 | 19 | del stacks[tid] 20 | return value 21 | except OverflowError: 22 | raise OverflowError, 'tail call, recursion depth %i' % count 23 | except MemoryError: 24 | raise MemoryError, 'tail call, recursion depth %i' % count 25 | else: 26 | stacks[tid].append((args, kwargs)) 27 | 28 | return wrapper 29 | 30 | if __name__ == '__main__': 31 | @tail 32 | def fib(i, current = 0, next = 1): 33 | if i == 0: 34 | return current 35 | else: 36 | return fib(i - 1, next, current + next) 37 | 38 | import time 39 | start = time.time() 40 | # 100k recursions is way over the python max recursion depth, but @tail doesn't care :) 41 | fib(100000) 42 | print 'Completed in %0.5f seconds.' % (time.time() - start) 43 | -------------------------------------------------------------------------------- /utils/hardlink/hardlink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | # given one or more directories, generates hardlink suggestions automatically 3 | # based on file size and a quick md5 hashes of a distributed data samples 4 | 5 | if [[ $# -lt 1 ]]; then 6 | echo "Usage: hardlink [dir...]" 7 | exit 1 8 | fi 9 | 10 | ref="" 11 | matches="" 12 | 13 | sample_count=5 14 | sample_size=1024 15 | 16 | # because we're 0-indexed 17 | let sample_count-- 18 | 19 | uname="$(uname)" 20 | case "$uname" in 21 | Linux) 22 | bytes='stat -c%s' 23 | md5='md5sum -b' 24 | ;; 25 | Darwin) 26 | bytes='stat -f%z' 27 | md5='md5' 28 | ;; 29 | *) 30 | echo "Unknown platform '$uname'." 31 | echo "Feel free to add functions for your platform and submit a patch" 32 | ;; 33 | esac 34 | 35 | # 36 | function range { 37 | tail "-c+$2" "$1" | head "-c$3" 38 | } 39 | 40 | # 41 | function sample { 42 | filesize="$2" 43 | 44 | i=0 45 | while ((i<=sample_count)); do 46 | start=$((filesize / sample_count * i)) 47 | range "$1" "$start" "$sample_size" 48 | let i++ 49 | done 50 | } 51 | 52 | # 53 | function add { 54 | if [[ -n "$ref" ]]; then 55 | ref="$ref 56 | $1" 57 | else 58 | ref="$1" 59 | fi 60 | } 61 | 62 | # 63 | function check { 64 | filesize=$($bytes "$1") 65 | hashed=$(sample "$1" "$filesize" | $md5 | awk {'print $1'}) 66 | 67 | comp="$filesize $hashed" 68 | match="$(echo "$ref" | grep " $comp\$")" 69 | if [[ -n "$match" ]]; then 70 | echo "$1" "$match" 71 | else 72 | add "$1 $comp" 73 | fi 74 | } 75 | 76 | # 77 | function scan { 78 | while IFS= read -r -d $'\0' file; do 79 | check "$file" 80 | done < <(find "$1" -type f -print0) 81 | } 82 | 83 | while read path; do 84 | scan "$path" 85 | done < <(echo "$@" | tr ' ' '\n' | uniq | xargs) # skip duplicate paths 86 | 87 | -------------------------------------------------------------------------------- /utils/binsplit/binsplit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import os 3 | import subprocess 4 | 5 | def binwalk(path): 6 | p = subprocess.Popen(['binwalk', path], stdout=subprocess.PIPE) 7 | return p.communicate('')[0] 8 | 9 | def split(out, path, start, end): 10 | print('%-15s 0x%x - 0x%x' % (os.path.basename(out), start, end)) 11 | with open(out, 'wb') as o: 12 | with open(path, 'rb') as f: 13 | f.seek(start) 14 | length = end - start 15 | align = length % 1024 16 | for i in xrange(0, length - align, 1024): 17 | o.write(f.read(1024)) 18 | if align: 19 | o.write(f.read(align)) 20 | 21 | def binsplit(path, walk): 22 | base = os.path.basename(path) 23 | i = 1 24 | out = base + '.split' 25 | while True: 26 | if os.path.exists(out): 27 | out = '%s.split.%d' % (base, i) 28 | i += 1 29 | continue 30 | try: 31 | os.mkdir(out) 32 | break 33 | except OSError as e: 34 | if os.path.exists(out): 35 | continue 36 | raise e 37 | 38 | last_pos = 0 39 | suffix = 'header' 40 | i = 0 41 | for line in walk.split('\n')[3:]: 42 | if not line.strip(): 43 | continue 44 | pos = int(line.split()[0]) 45 | split(os.path.join(out, '%d.%s' % (i, suffix)), path, last_pos, pos) 46 | suffix = line.split()[2].lower() 47 | last_pos = pos 48 | i += 1 49 | 50 | split(os.path.join(out, '%d.%s' % (i, suffix)), path, last_pos, os.stat(path).st_size) 51 | 52 | if __name__ == '__main__': 53 | import sys 54 | if len(sys.argv) != 2: 55 | print('Usage: %s ' % sys.argv[0]) 56 | sys.exit(1) 57 | 58 | print('Running binwalk.') 59 | out = binwalk(sys.argv[1]) 60 | print('--------') 61 | binsplit(sys.argv[1], out) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | meta 2 | ========== 3 | contains two vague categories of code: utilities and snippets 4 | 5 | * __utilities__: clever single-file solutions 6 | * aotool: create and dump Rosetta 2 .aot assemblies 7 | * autostrace: creates strace logs for all matching processes 8 | * beamsync: disable BeamSync on macOS 9 | * brew_altuser: install Homebrew as a different user on macOS 10 | * binsplit: splits a file into pieces based on the output of binwalk 11 | * dimmer: allows you to dim/undim monitors on Windows using hotkeys 12 | * dumpmem: dump all mapped memory of a process on macOS 13 | * gateway: print a list of interfaces, IPs, and gateway IPs 14 | * gitslurp: download all repositories from a GitHub organization 15 | * hardlink: searches directories and suggests identical files to hardlink 16 | * jaraudit: checks your Java dependencies for CVEs 17 | * macbinary: extracts a .zip, preserving \_\_MACOSX resource forks as MacBinary files 18 | * permute: builds chains of similar characters and permutes possible combinations 19 | * pidrun: run an executable with a specified PID 20 | * repl: create a repl by passing arguments to an existing command 21 | * retina: set unscaled resolution modes on a Retina MacBook 22 | * revmatch: find the closest matching upstream revisions for local vendored code 23 | * shotgun: quickly converts input to several formats 24 | * shotsum: run multiple checksums against input 25 | * sonosd: Sonos control daemon 26 | * taplist: View systemwide CGEventTap information on macOS. 27 | * tvsort: tries to automatically sort tv shows into an existing folder structure 28 | * vm: simplify managing and logging into a headless linux vmware instance 29 | 30 | * __snippets__: batteries not included 31 | * python 32 | * netreg: wrapper around samba's remote registry access 33 | * singleton: metaclass implementing Singleton classes 34 | * tailcall: decorator allowing tail recursion without hitting python's max recursion depth 35 | * threadpool: decorator capable of transparently threading calls to one or more functions, with the ability to store the return values for consumption. 36 | -------------------------------------------------------------------------------- /utils/tvsort/README.md: -------------------------------------------------------------------------------- 1 | tvsort 2 | ========= 3 | A Python script to intelligently sort television shows into folders based on filename. 4 | 5 | Usage 6 | ----- 7 | * Flags: 8 | * -m, --move: force moving of normal files (extracted files are always moved) 9 | * -d, --dry: dry run - only display what would be moved/copied 10 | * Source options: 11 | * /path/to/folder 12 | * rtorrent://localhost/scgi_path 13 | 14 | Process 15 | ----- 16 | * Parse all [target folders] for subfolders, and parse any .auto files inside (more on .auto files later) 17 | * Extract a series name, season number, and episode number from the each file/folder in [source]. 18 | * Match each series name exactly with a folder in one of the target folders, or skip that series for the entire run. 19 | * If the source path is a folder, check inside for archives to extract (currently only rar via 'unrar' works) and extract them, then check for video files to move (currently only a single resulting .avi or .mkv works) and set the copy flag to false for this file (because we extracted it ourselves, we assume moving instead of copying won't disrupt anything) 20 | * Copy (or move, if we extracted an archive) the video file we have decided to sort to [target folder]/[series]/Season [season]/[series] S[season]E[episode].[ext] 21 | 22 | .auto files 23 | ----- 24 | If a text file '.auto' exists in a series folder, it can override the series name match via regex and the resulting match. The regex is treated as case-insensitive. 25 | 26 | Example: 27 | 28 | regex: (dr\.?|doctor) who 29 | name: Season %(season)i/Dr. Who %(epstr)s 30 | 31 | Available format options: 32 | 33 | * %(season)i - replaced with the season number 34 | * %(episode)i - replaced with the episode number 35 | * %(name)s - replaced with the series name 36 | * %(epstr)s - replaced with the season+episode format: e.g. S01E01 37 | * %(season)02d - season number, left-padded to two digits with zeros 38 | * %(episode)02d - episode number, same as above 39 | 40 | Default naming scheme: 41 | 42 | * Season %(season)s/%(name)s %(epstr)s 43 | Result: Season 1/Name S01E01 44 | 45 | Example alternate naming scheme: 46 | 47 | * Season %(season)i/%(name)s %(season)ix%(episode)02d 48 | Result: Season 1/Name 1x01 49 | -------------------------------------------------------------------------------- /snippets/python/netreg.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Registry: 4 | def __init__(self, host, username, password): 5 | self.cmd = 'net rpc registry -S %s -U "%s%%%s"' % (host, username, password) 6 | 7 | def call(self, cmd, *args): 8 | args = '"' + '" "'.join(args) + '"' 9 | shell = ' '.join((self.cmd, cmd, args)) 10 | n, out, err = os.popen3(shell) 11 | return out.read() 12 | 13 | def join(self, *args): 14 | return '\\'.join(args) 15 | 16 | def enum(self, *base): 17 | base = self.join(*base) 18 | 19 | if base.count('HKEY_USERS') > 2: raise 20 | lines = self.call('enumerate', base) 21 | data = {} 22 | 23 | key = '' 24 | cur = {} 25 | for line in lines.split('\n'): 26 | if not line: 27 | if key: data[key] = cur 28 | key = '' 29 | cur = {} 30 | continue 31 | 32 | typ, value = line.strip().split('=', 1) 33 | typ, value = typ.strip(), value.strip() 34 | 35 | if typ == 'Keyname': 36 | key = value 37 | elif typ == 'Valuename': 38 | if value == '': 39 | value = '@' 40 | key = value 41 | 42 | cur[typ] = value 43 | 44 | if key: data[key] = cur 45 | return data 46 | 47 | def list(self, *base): 48 | return list(self.enum(*base)) 49 | 50 | def get(self, key, name): 51 | return self.enum(key)[name] 52 | 53 | def set(self, key, name, typ, *values): 54 | return self.call('setvalue', key, name, typ, *values) 55 | 56 | def delete(self, key, name): 57 | lines = self.call('deletevalue', key, name) 58 | return lines 59 | 60 | def walk(self, base): 61 | tree = {} 62 | keys = self.enum(base) 63 | for key in keys: 64 | if not 'Value' in keys[key]: 65 | tree[key] = self.walk(base, key) 66 | 67 | return tree 68 | 69 | if __name__ == '__main__': 70 | host = '' 71 | username = '' 72 | password = '' 73 | reg = Registry(host, username, password) 74 | for user in reg.enum('HKEY_USERS'): 75 | try: 76 | run = reg.join('HKEY_USERS', user, 'Software\\Microsoft\\Windows\\CurrentVersion\\Run') 77 | l = reg.list(run) 78 | if l: 79 | print l 80 | print reg.get(run, l[0])['Value'] 81 | print reg.enum(run) 82 | except KeyError: 83 | continue 84 | -------------------------------------------------------------------------------- /utils/dumpmem/dumpmem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void prot_mask_fill(char out[4], vm_prot_t prot) { 9 | memset(out, '-', 3); 10 | if (prot & VM_PROT_READ) out[0] = 'r'; 11 | if (prot & VM_PROT_WRITE) out[1] = 'w'; 12 | if (prot & VM_PROT_EXECUTE) out[2] = 'x'; 13 | } 14 | 15 | void dump_mem(int pid, const char *out_path) { 16 | mach_port_t port; 17 | kern_return_t err = task_for_pid(mach_task_self(), pid, &port); 18 | if (err != KERN_SUCCESS) { 19 | // TODO: print string error 20 | printf("task_for_pid error\n"); 21 | return; 22 | } 23 | 24 | mach_msg_type_number_t count; 25 | mach_port_t object_name; 26 | mach_vm_address_t address = 1, prev_address = 1; 27 | mach_vm_size_t vm_size; 28 | vm_region_basic_info_data_64_t info; 29 | 30 | while (1) { 31 | count = VM_REGION_BASIC_INFO_COUNT_64; 32 | err = mach_vm_region(port, &address, &vm_size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &object_name); 33 | if (err != KERN_SUCCESS) { 34 | printf("mach_vm_region error\n"); 35 | return; 36 | } 37 | char prot_mask[4] = {0}; 38 | prot_mask_fill(prot_mask, info.protection); 39 | 40 | prev_address = address; 41 | address += vm_size; 42 | 43 | // stop on wrap 44 | if (address < prev_address) { 45 | break; 46 | } 47 | 48 | vm_offset_t ptr; 49 | uint32_t size; 50 | if (vm_read(port, prev_address, vm_size, &ptr, &size)) { 51 | printf("vm_read error\n"); 52 | return; 53 | } 54 | char *name; 55 | asprintf(&name, "%s/0x%llx+0x%llx.%s", out_path, prev_address, vm_size, prot_mask); 56 | printf("%s\n", name); 57 | 58 | FILE *f = fopen(name, "w"); 59 | fwrite((void *)ptr, size, 1, f); 60 | fclose(f); 61 | 62 | vm_deallocate(port, ptr, size); 63 | } 64 | return; 65 | } 66 | 67 | void usage(const char *name) { 68 | printf("Usage: %s \n", name); 69 | } 70 | 71 | int main(int argc, char **argv) { 72 | if (argc != 3) { 73 | usage(argv[0]); 74 | return 1; 75 | } 76 | int pid = atol(argv[1]); 77 | if (pid <= 0) { 78 | usage(argv[0]); 79 | return 1; 80 | } 81 | if (mkdir(argv[2], 0755)) { 82 | fprintf(stderr, "mkdir '%s': ", argv[2]); 83 | perror(""); 84 | return 1; 85 | }; 86 | dump_mem(pid, argv[2]); 87 | } 88 | -------------------------------------------------------------------------------- /scripts/install_brew_altuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ue 2 | 3 | # you need to run this script as root 4 | if [[ "$(id -u)" != 0 ]]; then 5 | set -x 6 | exec sudo "$0" 7 | fi 8 | 9 | user=brew 10 | gid=12 # everyone=12 11 | 12 | # finds the first unused uid 13 | alloc_uid() { 14 | local n=502 15 | while id "$n" &>/dev/null; do 16 | let n++ 17 | done 18 | echo "$n" 19 | } 20 | 21 | remove_trailing_lines() { 22 | sed -i.bak -e :a -e '/^\n*$/{$d;N;};/\n$/ba' "$1" 23 | } 24 | 25 | # create user 26 | if id -u "$user" &>/dev/null; then 27 | echo "[-] User $user exists, skipping user creation" 28 | else 29 | echo "[+] Creating User: $user" 30 | dscl . create "/Users/$user" 31 | dscl . create "/Users/$user" UserShell /usr/bin/false # /usr/bin/false prevents login 32 | dscl . create "/Users/$user" RealName "Homebrew" 33 | uid=$(alloc_uid) 34 | dscl . create "/Users/$user" UniqueID "$uid" 35 | dscl . create "/Users/$user" PrimaryGroupID "$gid" 36 | dscl . create "/Users/$user" NFSHomeDirectory /usr/local/Cellar 37 | dscl . create "/Users/$user" IsHidden 1 38 | 39 | echo "[+] Created User: $user (uid=$uid gid=$gid)" 40 | fi 41 | 42 | # populate /usr/local 43 | echo "[+] Creating and setting permissions on directories in /usr/local" 44 | dirs=(bin Caskroom Cellar etc Frameworks Homebrew include lib opt sbin share var) 45 | cd /usr/local 46 | for dir in "${dirs[@]}"; do 47 | mkdir -p "$dir" 48 | chown brew:staff "$dir" 49 | chmod 755 "$dir" 50 | done 51 | 52 | # install homebrew 53 | echo "[+] Installing homebrew to /usr/local/Cellar" 54 | curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C Homebrew 55 | ln -fs ../Homebrew/bin/brew bin/brew 56 | chown -R "$user":"$gid" Homebrew bin/brew 57 | 58 | # set up sudobrew 59 | echo "[+] Creating /opt/sudobrew" 60 | mkdir -p /opt 61 | if [[ -e /opt/sudobrew ]]; then 62 | chflags noschg /opt/sudobrew 63 | rm -f /opt/sudobrew 64 | fi 65 | echo '#!/bin/sh 66 | cd / 67 | export EDITOR=vim 68 | export HOME=/tmp 69 | export HOMEBREW_NO_ANALYTICS=1 70 | exec sudo -E -u brew /usr/local/bin/brew "$@" 71 | ' > /opt/sudobrew 72 | chown root:staff /opt/sudobrew 73 | chmod 555 /opt/sudobrew 74 | chflags schg /opt/sudobrew 75 | 76 | # set up visudo 77 | tmpdir=$(mktemp -d) 78 | cd "$tmpdir" 79 | cp /etc/sudoers . 80 | sed -i.bak -e '/\/opt\/sudobrew/d' sudoers 81 | remove_trailing_lines sudoers 82 | echo >> sudoers 83 | echo "$SUDO_USER ALL=NOPASSWD: /opt/sudobrew *" >> sudoers 84 | visudo -cf sudoers 85 | cp sudoers /etc/sudoers 86 | cd / 87 | rm -rf "$tmpdir" 88 | 89 | # set up .bash_profile 90 | echo "[+] Update .bash_profile" 91 | cd "$HOME" 92 | if [[ -e .bash_profile ]]; then 93 | sed -i.bak -e '/brew() {/d' .bash_profile 94 | remove_trailing_lines .bash_profile 95 | echo >> .bash_profile 96 | else 97 | touch .bash_profile 98 | chown "$SUDO_UID:$SUDO_GID" .bash_profile 99 | fi 100 | echo 'brew() { sudo /opt/sudobrew "$@"; }' >> .bash_profile 101 | rm -f .bash_profile.bak 102 | 103 | # xcode-select --install 104 | # xcodebuild -license accept 105 | -------------------------------------------------------------------------------- /utils/brew_altuser/install_brew_altuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ue 2 | 3 | # you need to run this script as root 4 | if [[ "$(id -u)" != 0 ]]; then 5 | set -x 6 | exec sudo "$0" 7 | fi 8 | 9 | user=brew 10 | gid=12 # everyone=12 11 | 12 | # finds the first unused uid 13 | alloc_uid() { 14 | local n=502 15 | while id "$n" &>/dev/null; do 16 | let n++ 17 | done 18 | echo "$n" 19 | } 20 | 21 | remove_trailing_lines() { 22 | sed -i.bak -e :a -e '/^\n*$/{$d;N;};/\n$/ba' "$1" 23 | } 24 | 25 | # create user 26 | if id -u "$user" &>/dev/null; then 27 | echo "[-] User $user exists, skipping user creation" 28 | else 29 | echo "[+] Creating User: $user" 30 | dscl . create "/Users/$user" 31 | dscl . create "/Users/$user" UserShell /usr/bin/false # /usr/bin/false prevents login 32 | dscl . create "/Users/$user" RealName "Homebrew" 33 | uid=$(alloc_uid) 34 | dscl . create "/Users/$user" UniqueID "$uid" 35 | dscl . create "/Users/$user" PrimaryGroupID "$gid" 36 | dscl . create "/Users/$user" NFSHomeDirectory /usr/local/Homebrew 37 | dscl . create "/Users/$user" IsHidden 1 38 | 39 | echo "[+] Created User: $user (uid=$uid gid=$gid)" 40 | fi 41 | 42 | # populate /usr/local 43 | echo "[+] Creating and setting permissions on directories in /usr/local" 44 | dirs=(bin Caskroom Cellar etc Frameworks Homebrew include lib opt sbin share var) 45 | cd /usr/local 46 | for dir in "${dirs[@]}"; do 47 | mkdir -p "$dir" 48 | chown brew:staff "$dir" 49 | chmod 755 "$dir" 50 | done 51 | 52 | # install homebrew 53 | echo "[+] Extracting homebrew to /usr/local/Homebrew" 54 | curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C Homebrew 55 | chown -R "$user":"$gid" Homebrew 56 | ln -fs ../Homebrew/bin/brew bin/brew 57 | 58 | # set up sudobrew 59 | echo "[+] Creating /opt/sudobrew" 60 | mkdir -p /opt 61 | if [[ -e /opt/sudobrew ]]; then 62 | chflags noschg /opt/sudobrew 63 | rm -f /opt/sudobrew 64 | fi 65 | echo '#!/bin/sh 66 | cd / 67 | export EDITOR=vim 68 | export HOME=/tmp 69 | export HOMEBREW_NO_ANALYTICS=1 70 | exec sudo -E -u brew -- arch -x86_64 /usr/local/bin/brew "$@" 71 | ' > /opt/sudobrew 72 | chown root:staff /opt/sudobrew 73 | chmod 555 /opt/sudobrew 74 | chflags schg /opt/sudobrew 75 | 76 | # set up visudo 77 | sudofile=/etc/sudoers.d/brew_altuser 78 | rm -f "$sudofile" 79 | echo "$SUDO_USER ALL=NOPASSWD: /opt/sudobrew *" > "$sudofile" 80 | visudo -cf "$sudofile" || rm -f "$sudofile" 81 | 82 | # set up shell profile 83 | shell_profile() { 84 | rc="$1" 85 | echo "[+] Update $rc" 86 | cd "$HOME" 87 | if [[ -e "$rc" ]]; then 88 | sed -i.bak -e '/brew() {/d' "$rc" 89 | remove_trailing_lines "$rc" 90 | echo >> "$rc" 91 | else 92 | touch "$rc" 93 | chown "$SUDO_UID":"$SUDO_GID" "$rc" 94 | fi 95 | echo 'brew() { sudo /opt/sudobrew "$@"; }' >> "$rc" 96 | rm -f "${rc}.bak" 97 | } 98 | 99 | shell_profile "$HOME/.bash_profile" 100 | if [[ -e /bin/zsh ]]; then 101 | shell_profile "$HOME/.zshrc" 102 | fi 103 | 104 | # xcode-select --install 105 | # xcodebuild -license accept 106 | -------------------------------------------------------------------------------- /utils/revmatch/revmatch.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from contextlib import contextmanager 3 | import os 4 | import shutil 5 | import subprocess 6 | import sys 7 | import tempfile 8 | 9 | SvnMatch = None 10 | HgMatch = None 11 | 12 | @contextmanager 13 | def tmpdir(): 14 | d = tempfile.mkdtemp() 15 | yield d 16 | shutil.rmtree(d) 17 | 18 | def call(*args, **kwargs): 19 | stderr = kwargs.get('stderr') 20 | if stderr: 21 | stderr = subprocess.PIPE 22 | p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr) 23 | o1, o2 = p.communicate('') 24 | o = (o1 or '') + (o2 or '') 25 | if p.poll() and kwargs.get('throw') != False: 26 | print o 27 | raise subprocess.CalledProcessError(p.poll(), args, o) 28 | return o 29 | 30 | def diff(a, b): 31 | excludes = ['--exclude={}'.format(t) for t in repo_types] 32 | return call('diff', '-r', a, b, *excludes, throw=False) 33 | 34 | class Matcher: 35 | def __init__(self, repo): 36 | self.repo = repo 37 | 38 | class GitMatch(Matcher): 39 | def clone(self, target): 40 | call('git', 'clone', self.repo, target, stderr=True) 41 | 42 | def revs(self): 43 | with tmpdir() as tmp: 44 | self.clone(tmp) 45 | revs = call('git', '-C', tmp, 'log', '--pretty=format:%H').split('\n') 46 | total = len(revs) 47 | for rev in revs: 48 | call('git', '-C', tmp, 'checkout', rev, stderr=True) 49 | yield rev, tmp, total 50 | 51 | repo_types = { 52 | '.git': ('Git', GitMatch), 53 | '.svn': ('SVN', SvnMatch), 54 | '.hg': ('Mercurial', HgMatch), 55 | } 56 | 57 | def detect_repo(repo): 58 | for folder, tup in repo_types.items(): 59 | if os.path.exists(os.path.join(repo, folder)): 60 | return tup 61 | return 'unknown', None 62 | 63 | if __name__ == '__main__': 64 | if len(sys.argv) != 3: 65 | print 'Usage: {} '.format(sys.argv[0]) 66 | sys.exit(1) 67 | 68 | repo, target = sys.argv[1:3] 69 | repo_type, matcher = detect_repo(repo) 70 | if not matcher: 71 | print '{} is unsupported'.format(repo_type) 72 | sys.exit(1) 73 | 74 | matcher = matcher(repo) 75 | print 'Using {} backend.'.format(repo_type) 76 | matches = defaultdict(list) 77 | last = '' 78 | i = 0 79 | for rev, tmp, total in matcher.revs(): 80 | i += 1 81 | sys.stdout.write(len(last) * '\b') 82 | last = '{} {}/{}'.format(rev, i, total) 83 | sys.stdout.write(last) 84 | sys.stdout.flush() 85 | 86 | d = diff(tmp, target) 87 | matches[len(d)].append(rev) 88 | if len(d) == 0: 89 | break 90 | print 91 | 92 | if not matches: 93 | print 'No revisions found.' 94 | else: 95 | print 'Best candidate(s):'.format(min(len(matches), 5)) 96 | for k in sorted(matches.keys())[:5]: 97 | if k == 0: 98 | print '(Perfect match)', 99 | print 'Diff: {}, Hash: {}'.format(k, ', '.join(matches[k])) 100 | -------------------------------------------------------------------------------- /utils/permute/permute.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class Base: 4 | def __init__(self, possible): 5 | self.possible = set(possible) 6 | self.index = 0 7 | 8 | def current(self): 9 | return sorted(self.possible)[self.index] 10 | 11 | def next(self): 12 | value = self.current() 13 | self.index += 1 14 | if self.index >= len(self): 15 | self.index = 0 16 | return value 17 | 18 | def __contains__(self, value): 19 | return value in self.possible 20 | 21 | def __len__(self): 22 | return len(self.possible) 23 | 24 | def __str__(self): 25 | return self.current() 26 | 27 | def __repr__(self): 28 | return 'Base(['+','.join(self.possible)+'])' 29 | 30 | class Chain(Base): 31 | def __init__(self, possible): 32 | Base.__init__(self, possible) 33 | self.n = None 34 | 35 | def chain(self, next): 36 | self.n = next 37 | 38 | def next(self): 39 | Base.next(self) 40 | if self.index == 0: 41 | if self.n: 42 | self.n.next() 43 | 44 | class Single(Chain): 45 | def __init__(self, possible): 46 | Base.__init__(self, set(possible)) 47 | 48 | def current(self): 49 | return tuple(self.possible)[0] 50 | 51 | def next(self): 52 | self.index = abs(self.index - 1) 53 | 54 | class Word: 55 | def __init__(self, chains): 56 | self.chains = chains 57 | 58 | def __str__(self): 59 | return ''.join(str(chain) for chain in self.chains) 60 | 61 | similar = [ 62 | 'a4h', 63 | 'b8e3', 64 | 'L7', 65 | '96g', 66 | 'rRP', 67 | '1Il!|', 68 | ',.', 69 | "'`", 70 | 'CGdpqo', 71 | 'xk', 72 | 'mn', 73 | 'wm', 74 | 't7', 75 | '2z', 76 | 's5', 77 | '0o', 78 | '#H', 79 | 'yu', 80 | 'prk', 81 | 'AHV', 82 | 'FB', 83 | 'jyg', 84 | ';:', 85 | '?P', 86 | ')D', 89 | '$S', 90 | '@a', 91 | '-_=', 92 | '[]()', 93 | '\/i', 94 | ('m', 'rn') 95 | ] 96 | 97 | chains = {} 98 | for chars in similar: 99 | for char in chars: 100 | if not char in chains: 101 | chains[char] = set([char]) 102 | for sub in chars: 103 | for case in (sub.lower(), sub.upper()): 104 | chains[char].add(case) 105 | 106 | def make_chain(char): 107 | if char in chains: 108 | chars = chains[char] 109 | else: 110 | chars = [char] 111 | 112 | if len(chars) == 1: 113 | return Single(char) 114 | else: 115 | return Chain(chars) 116 | 117 | def permute(string): 118 | positions = [make_chain(char) for char in string] 119 | word = Word(positions) 120 | for i in xrange(len(positions)-1): 121 | positions[i].chain(positions[i+1]) 122 | 123 | first = positions[0] 124 | last = positions[-1] 125 | while last.index == 0: 126 | first.next() 127 | yield word 128 | 129 | while last.index != 0: 130 | first.next() 131 | yield word 132 | 133 | if __name__ == '__main__': 134 | import sys 135 | output = False 136 | 137 | if len(sys.argv) > 1: 138 | output = True 139 | for word in sys.argv[1:]: 140 | for result in permute(word): 141 | print result 142 | 143 | if not sys.stdin.isatty(): 144 | output = True 145 | for line in sys.stdin: 146 | for result in permute(line.strip()): 147 | print result 148 | 149 | if not output: 150 | print 'Usage: ./permute.py word ...' 151 | -------------------------------------------------------------------------------- /utils/vm/vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | root="/Volumes/Nifty/VMware Fusion" 4 | name="Ubuntu 64-bit" 5 | hostname="ubuntu" 6 | dir="/mnt/hgfs/Macintosh HD" 7 | chdir="$dir/$(pwd)" 8 | vm_path="$root/$name.vmwarevm/$name.vmx" 9 | 10 | if which -s vmrun; then 11 | vmrun="vmrun" 12 | else 13 | vmrun="/Applications/VMware Fusion.app/Contents/Library/vmrun" 14 | fi 15 | 16 | env="$(cat </dev/null 77 | if [[ $? -eq 0 ]]; then 78 | open -a chicken --args localhost:5902 79 | exit 0 80 | else 81 | echo "VNC doesn't seem to be open on localhost:5902" 82 | exit 2 83 | fi 84 | fi 85 | ;; 86 | status) 87 | echo -n "status: " 88 | if [[ -n "$pid" ]]; then 89 | echo "running (pid $pid)" 90 | else 91 | echo "stopped." 92 | fi 93 | ;; 94 | *) 95 | login -- "$cmd" "$@" 96 | ;; 97 | esac 98 | elif [[ $# -eq 0 ]]; then 99 | if [[ -z "$pid" ]]; then 100 | echo "Starting... " 101 | $vmrun start "$vm_path" nogui 102 | for i in $(seq 1 10); do 103 | ping -c1 -t5 "$hostname" &>/dev/null && break 104 | done 105 | ping -c1 -t1 "$hostname" &>/dev/null 106 | if [[ $? -ne 0 ]]; then 107 | echo "timeout." 108 | exit 1 109 | fi 110 | sleep 3 111 | echo " done." 112 | fi 113 | login 114 | fi 115 | 116 | -------------------------------------------------------------------------------- /utils/dimmer/dimmer.ahk: -------------------------------------------------------------------------------- 1 | ; Dimmer.ahk 2 | ; by Ryan Hileman (lunixbochs@gmail.com) 3 | ; This script allows you to dim/undim screens using a hotkey 4 | ; Press your configured modifier + number key to dim/undim a monitor 5 | 6 | ;; config 7 | ; available modifiers: 8 | ; ^ ctrl 9 | ; <^ left ctrl 10 | ; >^ right ctrl 11 | 12 | ; ! alt 13 | ; ! right alt 15 | 16 | ; + shift 17 | ; <+ left shift 18 | ; >+ right shift 19 | 20 | ; # windows logo 21 | ; ># right windows logo 22 | ; <# left windows logo 23 | 24 | modifiers = ^!+ 25 | 26 | ;; end config 27 | 28 | #NoEnv 29 | #NoTrayIcon 30 | #Persistent 31 | #SingleInstance 32 | 33 | dxva2 := DllCall("LoadLibrary", Str, "dxva2.dll", "Ptr") 34 | 35 | GetNumberOfPhysicalMonitorsFromHMONITOR := DllCall("GetProcAddress" 36 | , uint, dxva2 37 | , str, "GetNumberOfPhysicalMonitorsFromHMONITOR") 38 | 39 | GetPhysicalMonitorsFromHMONITOR := DllCall("GetProcAddress" 40 | , uint, dxva2 41 | , str, "GetPhysicalMonitorsFromHMONITOR") 42 | 43 | GetMonitorCapabilities := DllCall("GetProcAddress" 44 | , uint, dxva2 45 | , str, "GetMonitorCapabilities") 46 | 47 | GetMonitorBrightness := DllCall("GetProcAddress" 48 | , uint, dxva2 49 | , str, "GetMonitorBrightness") 50 | 51 | SetMonitorBrightness := DllCall("GetProcAddress" 52 | , uint, dxva2 53 | , str, "SetMonitorBrightness") 54 | 55 | ToggleBrightness(hMon) { 56 | global 57 | ; Find number of Physical Monitors 58 | DllCall(GetNumberOfPhysicalMonitorsFromHMONITOR, "int", hMon, "uint*", nMon) 59 | 60 | ; Get Physical Monitor from handle 61 | VarSetCapacity(Physical_Monitor, (A_PtrSize ? A_PtrSize : 4) + 128, 0) 62 | 63 | DllCall(GetPhysicalMonitorsFromHMONITOR 64 | , "int", hMon ; monitor handle 65 | , "uint", nMon ; monitor array size 66 | , "int", &Physical_Monitor) ; point to array with monitor 67 | 68 | hPhysMon := NumGet(Physical_Monitor) 69 | 70 | DllCall(GetMonitorBrightness, "int", hPhysMon, "uint*", minBright, "uint*", curBright, "uint*", maxBright) 71 | 72 | if (curBright > minBright) { 73 | DllCall(SetMonitorBrightness, "int", hPhysMon, "uint", minBright) 74 | } else { 75 | DllCall(SetMonitorBrightness, "int", hPhysMon, "uint", maxBright) 76 | } 77 | } 78 | 79 | EnumMonitor(hMonitor, hdcMonitor, lprcMonitor, dwData) { 80 | static mCount = 0 81 | mCount += 1 82 | if (dwData = mCount) { 83 | ToggleBrightness(hMonitor) 84 | mCount = 0 85 | return 0 86 | } 87 | return 1 88 | } 89 | 90 | callback := RegisterCallback("EnumMonitor") 91 | 92 | AdjustBrightness(mon) { 93 | global callback 94 | res := DllCall("EnumDisplayMonitors" 95 | , "int", 0 96 | , "int", 0 97 | , "ptr", callback 98 | , "int", mon) 99 | } 100 | 101 | ; hopefully you don't have more than 9 monitors and still care about individual brightness control ;) 102 | Loop, 9 { 103 | Hotkey, %modifiers%%A_Index%, HandleKey 104 | } 105 | 106 | OnExit Cleanup 107 | return 108 | 109 | HandleKey: 110 | key := SubStr(A_ThisHotkey, 0, 1) 111 | AdjustBrightness(key) 112 | return 113 | 114 | Cleanup: 115 | DllCall("FreeLibrary", "Ptr", dxva2) 116 | ExitApp 117 | -------------------------------------------------------------------------------- /utils/taplist/README.md: -------------------------------------------------------------------------------- 1 | taplist 2 | ==== 3 | 4 | View systemwide CGEventTap information on macOS. 5 | 6 | Building 7 | ---- 8 | 9 | sh taplist.c 10 | 11 | Example 12 | ---- 13 | 14 | ``` 15 | $ ./taplist 16 | 17 | - 337453826: 18 | enabled: true 19 | process: universalaccessd (331) 20 | options: kCGEventTapOptionListenOnly (0) 21 | location: kCGSessionEventTap 22 | mask: kCGEventLeftMouseDown | kCGEventLeftMouseUp | kCGEventRightMouseDown | kCGEventRightMouseUp | kCGEventMouseMoved | kCGEventLeftMouseDragged | kCGEventR 23 | ightMouseDragged | kCGEventScrollWheel | kCGEventTabletPointer | kCGEventTabletProximity | kCGEventOtherMouseDown | kCGEventOtherMouseUp | kCGEventOtherMouseDragged 24 | | NX_ZOOM (0xffc000fe) 25 | latency: min=0.000ms avg=0.000ms max=0.000ms 26 | 27 | - 400000569: 28 | enabled: false 29 | process: universalaccessd (331) 30 | options: kCGEventTapOptionListenOnly (0) 31 | location: kCGSessionEventTap 32 | mask: kCGEventLeftMouseDown | kCGEventLeftMouseUp | kCGEventRightMouseDown | kCGEventRightMouseUp | kCGEventMouseMoved | kCGEventLeftMouseDragged | kCGEventR 33 | ightMouseDragged | kCGEventScrollWheel | kCGEventTabletPointer | kCGEventTabletProximity | kCGEventOtherMouseDown | kCGEventOtherMouseUp | kCGEventOtherMouseDragged 34 | | NX_ZOOM (0x1fc000fe) 35 | latency: min=0.000ms avg=0.000ms max=0.000ms 36 | 37 | - 1025202362: 38 | enabled: true 39 | process: ViewBridgeAuxiliary (480) 40 | options: kCGEventTapOptionDefault (0x1) 41 | location: kCGSessionEventTap 42 | mask: kCGEventKeyDown | kCGEventKeyUp | kCGEventFlagsChanged (0x1c00) 43 | latency: min=0.000ms avg=0.000ms max=0.000ms 44 | 45 | - 1189641421: 46 | enabled: true 47 | process: NotificationCenter (390) 48 | options: kCGEventTapOptionDefault (0x1) 49 | location: kCGAnnotatedSessionEventTap 50 | mask: (0x80000000) 51 | latency: min=0.000ms avg=0.000ms max=0.000ms 52 | 53 | - 1649760492: 54 | enabled: true 55 | process: ViewBridgeAuxiliary (377) 56 | options: kCGEventTapOptionDefault (0x1) 57 | location: kCGSessionEventTap 58 | mask: kCGEventKeyDown | kCGEventKeyUp | kCGEventFlagsChanged (0x1c00) 59 | latency: min=0.000ms avg=0.000ms max=0.000ms 60 | 61 | - 719885386: 62 | enabled: true 63 | process: universalaccessd (331) 64 | options: kCGEventTapOptionDefault (0x1) 65 | location: kCGSessionEventTap 66 | mask: kCGEventKeyDown | kCGEventKeyUp | kCGEventFlagsChanged (0x1c00) 67 | latency: min=0.000ms avg=0.000ms max=0.000ms 68 | 69 | - 424238335: 70 | enabled: false 71 | process: universalaccessd (331) 72 | options: kCGEventTapOptionDefault (0x1) 73 | location: kCGSessionEventTap 74 | mask: kCGEventKeyDown | kCGEventKeyUp | kCGEventFlagsChanged (0x1c00) 75 | latency: min=0.000ms avg=0.000ms max=0.000ms 76 | 77 | - 1957747793: 78 | enabled: false 79 | process: universalaccessd (331) 80 | options: kCGEventTapOptionDefault (0x1) 81 | location: kCGSessionEventTap 82 | mask: kCGEventMouseMoved | kCGEventLeftMouseDragged | kCGEventRightMouseDragged | kCGEventOtherMouseDragged (0x80000e0) 83 | latency: min=0.000ms avg=0.000ms max=0.000ms 84 | 85 | - 1714636915: 86 | enabled: true 87 | process: universalaccessd (331) 88 | options: kCGEventTapOptionDefault (0x1) 89 | location: kCGSessionEventTap 90 | mask: NX_SYSDEFINED | NX_ZOOM (0x10204000) 91 | latency: min=0.000ms avg=0.000ms max=0.000ms 92 | ``` 93 | -------------------------------------------------------------------------------- /scripts/install_port_altuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ue 2 | 3 | # you need to run this script as root 4 | if [[ "$(id -u)" != 0 ]]; then 5 | set -x 6 | exec sudo "$0" 7 | fi 8 | 9 | user=port 10 | gid=12 # everyone=12 11 | njobs=$(getconf _NPROCESSORS_ONLN) 12 | 13 | # finds the first unused uid 14 | alloc_uid() { 15 | local n=502 16 | while id "$n" &>/dev/null; do 17 | let n++ 18 | done 19 | echo "$n" 20 | } 21 | 22 | remove_trailing_lines() { 23 | sed -i '' -e :a -e '/^\n*$/{$d;N;};/\n$/ba' "$1" 24 | } 25 | 26 | # create user 27 | if id -u "$user" &>/dev/null; then 28 | echo "[-] User $user exists, skipping user creation" 29 | else 30 | echo "[+] Creating User: $user" 31 | dscl . create "/Users/$user" 32 | dscl . create "/Users/$user" UserShell /usr/bin/false # /usr/bin/false prevents login 33 | dscl . create "/Users/$user" RealName "MacPorts" 34 | uid=$(alloc_uid) 35 | dscl . create "/Users/$user" UniqueID "$uid" 36 | dscl . create "/Users/$user" PrimaryGroupID "$gid" 37 | dscl . create "/Users/$user" NFSHomeDirectory /opt/local/var/macports/home 38 | dscl . create "/Users/$user" IsHidden 1 39 | 40 | echo "[+] Created User: $user (uid=$uid gid=$gid)" 41 | fi 42 | 43 | tmpdir=$(mktemp -d) 44 | cd "$tmpdir" 45 | 46 | # populate /usr/local 47 | echo "[+] Creating and setting permissions on directories in /opt/local" 48 | mkdir -p /opt/local 49 | 50 | # install macports 51 | echo "[+] Installing macports to /opt/local" 52 | curl -L https://github.com/macports/macports-base/releases/download/v2.10.2/MacPorts-2.10.2.tar.bz2 | tar xz --strip 1 53 | ./configure --prefix=/opt/local --with-install-user=port --with-install-group=everyone 54 | make -j"$njobs" 55 | make install 56 | echo "+universal" >> /opt/local/etc/macports/variants.conf 57 | sed -i '' -e 's/^#buildmakejobs/buildmakejobs/' /opt/local/etc/macports/macports.conf 58 | chown -R "$user:everyone" /opt/local 59 | 60 | # set up sudoport 61 | echo "[+] Creating /opt/sudoport" 62 | mkdir -p /opt 63 | if [[ -e /opt/sudoport ]]; then 64 | chflags noschg /opt/sudoport 65 | rm -f /opt/sudoport 66 | fi 67 | echo '#!/bin/sh 68 | cd / 69 | export EDITOR=vim 70 | export HOME=/tmp 71 | exec sudo -E -u '"$user"' /opt/local/bin/port "$@" 72 | ' > /opt/sudoport 73 | chown root:staff /opt/sudoport 74 | chmod 555 /opt/sudoport 75 | chflags schg /opt/sudoport 76 | mkdir -p /opt/local/sudobin 77 | ln -fs /opt/sudoport /opt/local/sudobin/port 78 | 79 | # set up visudo 80 | cp /etc/sudoers . 81 | sed -i '' -e '/\/opt\/sudoport/d' sudoers 82 | remove_trailing_lines sudoers 83 | echo >> sudoers 84 | echo "$SUDO_USER ALL=NOPASSWD: /opt/sudoport *" >> sudoers 85 | visudo -cf sudoers 86 | cp sudoers /etc/sudoers 87 | rm -f sudoers 88 | cd / 89 | rm -rf "$tmpdir" 90 | 91 | # set up .bash_profile and .zprofile 92 | setup_profile() { 93 | local profile="$1" 94 | echo "[+] Update $profile" 95 | cd "$HOME" 96 | if [[ -e "$profile" ]]; then 97 | sed -i '' -e '/port() {/d' "$profile" 98 | remove_trailing_lines "$profile" 99 | echo >> "$profile" 100 | else 101 | touch "$profile" 102 | chown "$SUDO_UID:$SUDO_GID" "$profile" 103 | fi 104 | echo 'port() { sudo /opt/sudoport "$@"; }; export PATH="/opt/local/sudobin:/opt/local/bin:/opt/local/sbin:$PATH"' >> "$profile" 105 | } 106 | setup_profile .bash_profile 107 | setup_profile .zprofile 108 | 109 | # xcode-select --install 110 | # xcodebuild -license accept 111 | -------------------------------------------------------------------------------- /utils/taplist/taplist.c: -------------------------------------------------------------------------------- 1 | #/* 2 | echo "Building ./taplist" 3 | gcc "$0" -framework CoreGraphics -o taplist 4 | exit 0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | char *process_name(int pid) { 12 | char name[PROC_PIDPATHINFO_MAXSIZE] = {0}; 13 | if (proc_name(pid, name, 1024) <= 0) { 14 | if (proc_pidpath(pid, name, 1024) > 0) { 15 | return strdup(basename(name)); 16 | } else { 17 | return strdup(""); 18 | } 19 | } 20 | return strdup(name); 21 | } 22 | 23 | struct event { 24 | char *name; 25 | uint64_t num; 26 | uint64_t bit; 27 | }; 28 | #define ev(name) {#name, name, CGEventMaskBit(name)} 29 | struct event events[] = { 30 | ev(kCGEventNull), 31 | ev(kCGEventLeftMouseDown), 32 | ev(kCGEventLeftMouseUp), 33 | ev(kCGEventRightMouseDown), 34 | ev(kCGEventRightMouseUp), 35 | ev(kCGEventMouseMoved), 36 | ev(kCGEventLeftMouseDragged), 37 | ev(kCGEventRightMouseDragged), 38 | ev(kCGEventKeyDown), 39 | ev(kCGEventKeyUp), 40 | ev(kCGEventFlagsChanged), 41 | ev(kCGEventScrollWheel), 42 | ev(kCGEventTabletPointer), 43 | ev(kCGEventTabletProximity), 44 | ev(kCGEventOtherMouseDown), 45 | ev(kCGEventOtherMouseUp), 46 | ev(kCGEventOtherMouseDragged), 47 | // NX events: 48 | ev(NX_KITDEFINED), 49 | ev(NX_SYSDEFINED), 50 | ev(NX_APPDEFINED), 51 | ev(NX_ZOOM), 52 | }; 53 | #undef ev 54 | 55 | void print_mask(uint64_t mask) { 56 | if (mask == kCGEventMaskForAllEvents) { 57 | printf("kCGEventMaskForAllEvents (%#llx)\n", mask); 58 | return; 59 | } 60 | bool first = true; 61 | for (int i = 0; i < sizeof(events) / sizeof(events[0]); i++) { 62 | if (mask & events[i].bit) { 63 | if (!first) printf(" | "); 64 | printf("%s", events[i].name); 65 | first = false; 66 | } 67 | } 68 | if (!first) printf(" "); 69 | printf("(%#llx)\n", mask); 70 | } 71 | 72 | const char *location_name(uint32_t location) { 73 | switch (location) { 74 | case kCGHIDEventTap: return "kCGHIDEventTap"; 75 | case kCGSessionEventTap: return "kCGSessionEventTap"; 76 | case kCGAnnotatedSessionEventTap: return "kCGAnnotatedSessionEventTap"; 77 | default: return ""; 78 | } 79 | } 80 | 81 | int main() { 82 | uint32_t count = 0; 83 | CGError err = 0; 84 | if ((err = CGGetEventTapList(0, NULL, &count))) { 85 | printf("error fetching event taps: %d\n", err); 86 | return -1; 87 | } 88 | CGEventTapInformation *taps = calloc(sizeof(CGEventTapInformation), count); 89 | if ((err = CGGetEventTapList(count, taps, &count))) { 90 | printf("error fetching event taps: %d\n", err); 91 | return -1; 92 | } 93 | for (uint32_t i = 0; i < count; i++) { 94 | CGEventTapInformation *tap = &taps[i]; 95 | printf("- %d:\n", tap->eventTapID); 96 | char *process = process_name(tap->tappingProcess); 97 | char *target = process_name(tap->processBeingTapped); 98 | 99 | printf(" enabled: %s\n", tap->enabled ? "true" : "false"); 100 | printf(" process: %s (%d)\n", process, tap->tappingProcess); 101 | if (tap->processBeingTapped) 102 | printf(" target: %s (%d)\n", target, tap->processBeingTapped); 103 | printf(" options: %s (%#x)\n", (tap->options&1 ? "kCGEventTapOptionDefault" : "kCGEventTapOptionListenOnly"), tap->options); 104 | printf(" location: %s\n", location_name(tap->tapPoint)); 105 | printf(" mask: "); 106 | print_mask(tap->eventsOfInterest); 107 | printf(" latency: min=%.3fms avg=%.3fms max=%.3fms\n", tap->minUsecLatency/1000, tap->avgUsecLatency/1000, tap->maxUsecLatency/1000); 108 | printf("\n"); 109 | 110 | free(process); 111 | free(target); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /utils/shotsum/shotsum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import argparse 4 | import ctypes 5 | import hashlib 6 | import itertools 7 | import sys 8 | import zlib 9 | 10 | try: 11 | import crcmod 12 | except ImportError: 13 | crcmod = None 14 | 15 | def pad(data, num, c='\0'): 16 | align = num - len(data) % num 17 | if align != num: 18 | return data + align * c 19 | else: 20 | return data 21 | 22 | def negate(n): 23 | return n ^ (2 ** 32 - 1) 24 | 25 | def _hex(n): 26 | h = hex(n).rstrip('L') 27 | if len(h) % 2: 28 | h = '0x0' + h[2:] 29 | 30 | return h 31 | 32 | def netgear(data): 33 | total = sum(ord(c) for c in data[::2]) 34 | chk = (total & 0xFFFF) + (total >> 16) 35 | chk = ~((chk >> 16) + chk) & 0xFFFF 36 | return chk 37 | 38 | def checksums(data, use_all=True): 39 | sums = {} 40 | if use_all and crcmod: 41 | names = [c[0] for c in crcmod.predefined._crc_definitions_table] 42 | for name in names: 43 | crc = crcmod.predefined.Crc(name) 44 | crc.update(data) 45 | sums[name] = _hex(crc.crcValue) 46 | else: 47 | crc32 = ctypes.c_uint32(zlib.crc32(data)).value 48 | sums['crc32'] = _hex(crc32) 49 | sums['crc32~'] = _hex(negate(crc32)) 50 | 51 | if use_all: 52 | adler32 = ctypes.c_uint32(zlib.adler32(data)).value 53 | sums['adler32'] = _hex(adler32) 54 | 55 | sums['netgear'] = _hex(netgear(data)) 56 | normal = ('md5', 'sha1') 57 | openssl = ('md4', 'mdc2', 'rmd160', 'sha') 58 | extra = ('sha224', 'sha256', 'sha384', 'sha512') + openssl 59 | for name in normal + extra: 60 | if not (name in extra) or use_all: 61 | try: 62 | func = hashlib.new(name) 63 | func.update(data) 64 | sums[name] = '0x' + func.hexdigest() 65 | except: 66 | import traceback 67 | print traceback.format_exc() 68 | pass 69 | 70 | return sums 71 | 72 | filters = { 73 | 'stripped': lambda x: x.strip(), 74 | 'null-stripped': lambda x: x.strip('\0'), 75 | '0xFF-stripped': lambda x: x.strip('\xFF'), 76 | '8-byte null-padded': lambda x: pad(x, 8), 77 | '8-byte 0xFF-padded': lambda x: pad(x, 8, c='\xFF'), 78 | } 79 | 80 | def shotsum(data, use_all=False): 81 | sections = {} 82 | already = set() 83 | for i in xrange(len(filters)): 84 | for choices in itertools.permutations(filters, i): 85 | names = ', '.join(choices) 86 | mangled = data 87 | for name in choices: 88 | data = filters[name](mangled) 89 | 90 | if mangled in already: 91 | continue 92 | else: 93 | sections[names] = checksums(mangled, use_all=use_all) 94 | already.add(mangled) 95 | 96 | return sections 97 | 98 | def pretty(data, use_all=False): 99 | sums = shotsum(data, use_all=use_all) 100 | for name, section in sorted(sums.items()): 101 | print 102 | if name: 103 | print '%s:' % name 104 | else: 105 | print 'unmodified:' 106 | 107 | longest = max(len(k) for k in section.keys()) 108 | for key, value in sorted(section.items()): 109 | print ' %s: %s' % (key.rjust(longest), value) 110 | 111 | if __name__ == '__main__': 112 | parser = argparse.ArgumentParser( 113 | description='run multiple checksums/hashes against input') 114 | parser.add_argument('-a', '--all', 115 | help='Use all available algorithms', action='store_true') 116 | parser.add_argument('filename', 117 | help='Input file (can also use stdin).', nargs='?') 118 | args = parser.parse_args() 119 | 120 | if sys.stdin.isatty(): 121 | if args.filename: 122 | with open(args.filename) as f: 123 | pretty(f.read(), use_all=args.all) 124 | else: 125 | parser.print_help() 126 | sys.exit(1) 127 | 128 | else: 129 | pretty(sys.stdin.read(), use_all=args.all) 130 | -------------------------------------------------------------------------------- /utils/sonosd/sonosd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import redis 4 | from soco import SoCo, SonosDiscovery 5 | from soco.exceptions import SoCoException 6 | import threading 7 | import time 8 | import traceback 9 | 10 | EXCLUDE = [] 11 | 12 | 13 | def get_devices(): 14 | ips = set() 15 | for ip in SonosDiscovery().get_speaker_ips(): 16 | ips.add(ip) 17 | 18 | return [SoCo(ip) for ip in ips] 19 | 20 | 21 | def find_master(devices): 22 | for d in devices: 23 | if d.get_speaker_info()['zone_name'].lower() == 'master': 24 | return d 25 | 26 | return sorted(devices, lambda d: d.get_speaker_info()['zone_name'])[0] 27 | 28 | 29 | class Volume(object): 30 | def __init__(self, device): 31 | self.device = device 32 | self.state = device.volume() 33 | self.last = time.time() 34 | self.lock = threading.RLock() 35 | 36 | @property 37 | def volume(self): 38 | now = time.time() 39 | if self.last < now - 5: 40 | self.last = now 41 | self.state = self.device.volume() 42 | return self.state 43 | 44 | @volume.setter 45 | def volume(self, new): 46 | self.last = time.time() 47 | self.state = new 48 | self.async_volume(new) 49 | 50 | def async_volume(self, new): 51 | def change(new): 52 | with self.lock: 53 | self.device.volume(new) 54 | threading.Thread(target=change, args=(new,)).start() 55 | 56 | 57 | class Daemon: 58 | def __init__(self): 59 | self.volume = 0 60 | self.volume_lock = threading.RLock() 61 | 62 | def volume_thread(self): 63 | devices = [Volume(d) for d in self.devices] 64 | while True: 65 | time.sleep(0.05) 66 | with self.volume_lock: 67 | change = self.volume 68 | self.volume = 0 69 | 70 | if not change: 71 | continue 72 | 73 | volumes = [d.volume for d in devices] 74 | if any((v == 0 and change < 0) or (v == 100 and change > 0) for v in volumes): 75 | continue 76 | 77 | for d in devices: 78 | d.volume += change 79 | 80 | def change_volume(self, by): 81 | with self.volume_lock: 82 | self.volume += by 83 | 84 | def pump(self): 85 | self.devices = [ 86 | d for d in get_devices() 87 | if d.get_speaker_info().get('zone_name').lower() not in EXCLUDE 88 | ] 89 | self.sonos = find_master(self.devices) 90 | self.info = self.sonos.get_speaker_info() 91 | thread = threading.Thread(target=self.volume_thread) 92 | thread.daemon = True 93 | thread.start() 94 | 95 | print 'connected' 96 | 97 | self.redis = redis.StrictRedis() 98 | ps = self.redis.pubsub() 99 | ps.subscribe('sonos') 100 | for item in ps.listen(): 101 | if item['type'] == 'message': 102 | try: 103 | self.handle(item['data']) 104 | except SoCoException as e: 105 | print 'Sonos Error:', e 106 | 107 | def handle(self, cmd): 108 | sonos = self.sonos 109 | if cmd == 'play': 110 | state = sonos.get_current_transport_info() or {} 111 | if state.get('current_transport_state') == 'PAUSED_PLAYBACK': 112 | sonos.play() 113 | else: 114 | sonos.pause() 115 | elif cmd == 'vol_up': 116 | self.change_volume(1) 117 | elif cmd == 'vol_down': 118 | self.change_volume(-1) 119 | elif cmd == 'next': 120 | self.sonos.next() 121 | elif cmd == 'prev': 122 | self.sonos.previous() 123 | elif cmd == 'group': 124 | self.sonos.switch_to_line_in() 125 | self.sonos.play() 126 | for device in self.devices: 127 | if device != self.sonos: 128 | device.join(self.info['uid']) 129 | 130 | if __name__ == '__main__': 131 | Daemon().pump() 132 | -------------------------------------------------------------------------------- /utils/macbinary/macbinary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # copyright (c) 2011 Ryan Hileman 3 | # released into the Public Domain for unlimited use without warranty 4 | 5 | import struct 6 | 7 | import os 8 | import datetime 9 | import time 10 | from zipfile import ZipFile 11 | 12 | class StructBuffer: 13 | def __init__(self, data): 14 | self.data = data 15 | self.pos = 0 16 | 17 | def read(self, fmt): 18 | size = struct.calcsize('>'+fmt) 19 | if self.pos + size >= len(self.data): 20 | raise IndexError 21 | 22 | ret = struct.unpack(fmt, self.data[self.pos:self.pos+size]) 23 | self.pos += size 24 | return ret 25 | 26 | def seek(self, pos): 27 | self.pos = pos 28 | 29 | class MacBinary: 30 | def __init__(self, filename = ''): 31 | self.filename = filename 32 | self.type = 0 33 | self.creator = 0 34 | self.flags = 0 35 | self.xpos = 0 36 | self.ypos = 0 37 | self.fid = 0 38 | self.protected = 0 39 | self.creation = 0 40 | self.modified = 0 41 | self.oid = 0 42 | 43 | self.data = '' 44 | self.res = '' 45 | 46 | @classmethod 47 | def decode(cls, data): 48 | head = StructBuffer(data[:128]) 49 | 50 | head.seek(1) 51 | flen, = head.read('B') 52 | filename, = head.read('%is' % flen) 53 | bin = cls(filename) 54 | 55 | head.seek(65) 56 | bin.type, = head.read('I') 57 | bin.creator, = head.read('I') 58 | bin.flags, = head.read('B') 59 | 60 | head.seek(74) 61 | bin.xpos, = head.read('H') 62 | bin.ypos, = head.read('H') 63 | bin.fid, = head.read('H') 64 | bin.protected, = head.read('B') 65 | 66 | head.seek(83) 67 | datalen, = head.read('I') 68 | reslen, = head.read('I') 69 | 70 | bin.creation, = head.read('I') 71 | bin.modified, = head.read('I') 72 | 73 | head.seek(125) 74 | bin.oid, = head.read('H') 75 | 76 | bin.data = data[128:128+datalen] 77 | bin.res = data[128+datalen:128+datalen+reslen] 78 | 79 | return bin 80 | 81 | def encode(self): 82 | fmt = ''.join(('>', 'xB', '63s', '2IBx3H', 'Bx', '4I', '27x', 'H')) 83 | args = ( 84 | len(self.filename), self.filename, 85 | self.type, self.creator, self.flags, self.xpos, self.ypos, self.fid, 86 | self.protected, len(self.data), len(self.res), self.creation, self.modified, 87 | self.oid 88 | ) 89 | 90 | head = struct.pack(fmt, *args) 91 | 92 | dlen = len(self.data) + 128 - (len(self.data) % 128) 93 | rlen = len(self.res) + 128 - (len(self.res) % 128) 94 | 95 | return head + struct.pack('%is%is' % (dlen, rlen), self.data, self.res) 96 | 97 | def unzip(filename): 98 | z = ZipFile(filename) 99 | names = z.namelist() 100 | for path in names: 101 | if path.startswith('__MACOSX/'): 102 | continue 103 | 104 | base, name = os.path.split(path) 105 | 106 | if name.startswith('._') and\ 107 | '%s/' % name.replace('._', '', 1) in names: 108 | continue 109 | 110 | double = os.path.join('__MACOSX', base, '._' + name) 111 | if double in names: 112 | print '=> %s.bin' % path 113 | 114 | info = z.getinfo(path) 115 | 116 | bin = MacBinary(name) 117 | bin.data = z.open(path, 'r').read() 118 | bin.res = z.open(double, 'r').read() 119 | 120 | modified = datetime.datetime(*info.date_time) 121 | bin.modified = time.mktime(modified.timetuple()) 122 | bin.created = time.time() 123 | 124 | if not os.path.exists(base): 125 | os.makedirs(base) 126 | 127 | with open('%s.bin' % path.rstrip('\r'), 'wb') as f: 128 | f.write(bin.encode()) 129 | else: 130 | print '-> %s' % path 131 | z.extract(path) 132 | 133 | if __name__ == '__main__': 134 | import sys 135 | 136 | if len(sys.argv) > 1: 137 | for arg in sys.argv[1:]: 138 | unzip(arg) 139 | else: 140 | print 'Usage: macbinary.py [zipfile...]' 141 | print 'Extracts a .zip, creating MacBinary files from AppleDouble resource forks' 142 | -------------------------------------------------------------------------------- /utils/shotgun/shotgun: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # shotgun: quickly and intelligently convert input to several formats 3 | 4 | import re 5 | import sys 6 | import base64 7 | import struct 8 | import binascii 9 | 10 | hx = lambda s: ' '.join('%02x' % ord(c) for c in s) 11 | printable = lambda s: re.sub(r'[^\x20-\x7E]', '.', s) 12 | 13 | def hex_format(data): 14 | ret = ['hex:'] 15 | for i in xrange(0, len(data), 16): 16 | s = data[i:i+16] 17 | s1, s2 = s[0:8], s[8:16] 18 | ret.append(' %08x %s %s |%s|' % ( 19 | i, hx(s1).ljust(23), s2 and hx(s2).ljust(23) or '', printable(s) 20 | )) 21 | 22 | ret.append(' %08x' % len(data)) 23 | 24 | return '\n'.join(ret) 25 | 26 | def endian_swap(num): 27 | if num <= 2 ** 32: 28 | packed = struct.pack('I', packed)[0] 30 | else: 31 | packed = struct.pack('Q', packed)[0] 33 | 34 | def numbers(num): 35 | # TODO: binary int <--> float? 36 | swapped = endian_swap(num) 37 | ret = [ 38 | ' bin: %12s' % bin(num).replace('0b', '', 1), 39 | # octal is disabled to save space 40 | # ' oct: %12s' % oct(num), 41 | ' int: %12s | %s' % (int(num), int(swapped)), 42 | ' hex: %12s | %s' % (hex(num), hex(swapped)) 43 | ] 44 | 45 | return '\n'.join(str(x) for x in ret) 46 | 47 | def unhex(hexd): 48 | if isinstance(hexd, int): 49 | hexd = hex(hexd) 50 | if hexd.startswith('0x'): 51 | hexd = hexd[2:] 52 | if len(hexd) % 2: 53 | hexd = '0' + hexd 54 | return printable(binascii.unhexlify(hexd)) 55 | 56 | def convert(data): 57 | # strip whitespace and nulls for numerical representation 58 | filtered = re.sub(r'[\s\0]', '', data) 59 | 60 | ret = [] 61 | 62 | # don't know if octal is actually useful 63 | octf = False and re.match(r'^0?([0-7]+)$', filtered) 64 | 65 | binf = re.match(r'^(0b)?([01]+)$', filtered) 66 | decf = re.match(r'^(\d+)$', filtered) 67 | hexf = re.match(r'^(0x)?([0-9A-Fa-f]+)$', filtered) 68 | b64f = re.match(r'^([A-Za-z\d+/]+)(={1,3})?$', filtered) 69 | 70 | ascii = hex_format(data) 71 | try: 72 | hdata = binascii.hexlify(data) 73 | bint = int(hdata, 16) 74 | ascii += '\n\n' + numbers(bint) 75 | except Exception: 76 | pass 77 | ret.append(('ascii', ascii)) 78 | 79 | if b64f: 80 | b64 = b64f.group(1) 81 | if len(b64) >= 2: 82 | # add padding if needed 83 | b64 += (len(b64) % 4) * '=' 84 | try: 85 | ascii = base64.b64decode(b64) 86 | if b64 == base64.b64encode(ascii): 87 | ret.append(('base64', hex_format(ascii))) 88 | except TypeError: 89 | pass 90 | 91 | if binf: 92 | n = int(binf.group(2), 2) 93 | ret.append(('bin', numbers(n))) 94 | 95 | if octf: 96 | n = int(octf.group(1), 8) 97 | ret.append(('oct', numbers(n))) 98 | 99 | if decf: 100 | n = int(decf.group(1), 10) 101 | ret.append(( 102 | 'dec', 103 | numbers(n) + '\n' 104 | ' ascii: ' + unhex(n) 105 | )) 106 | 107 | if hexf: 108 | hexd = hexf.group(2) 109 | n = int(hexd, 16) 110 | 111 | ret.append(( 112 | 'hex', 113 | numbers(n) + '\n' 114 | ' ascii: ' + unhex(hexd) 115 | )) 116 | 117 | return ret 118 | 119 | def pretty(data): 120 | ret = convert(data) 121 | print 122 | print 'length:', len(data) 123 | 124 | for key, value in ret: 125 | print 126 | print '---====[ %s ]====---' % key 127 | print value 128 | 129 | print 130 | 131 | if __name__ == '__main__': 132 | if not sys.stdin.isatty(): 133 | data = sys.stdin.read() 134 | pretty(data) 135 | elif len(sys.argv) > 1: 136 | data = ' '.join(sys.argv[1:]) 137 | pretty(data) 138 | else: 139 | try: 140 | print 'Shotgun:', 141 | print 'quickly and intelligently', 142 | print 'convert input to several formats' 143 | print '(^D or ^C to exit)' 144 | print 145 | while True: 146 | print '-' * 79 147 | line = raw_input(' ?> ') 148 | pretty(line) 149 | 150 | except KeyboardInterrupt: 151 | pass 152 | except EOFError: 153 | pass 154 | 155 | print 156 | -------------------------------------------------------------------------------- /utils/aotool/aotool.c: -------------------------------------------------------------------------------- 1 | #/* 2 | echo "Building ./aotool" 3 | gcc -arch x86_64 "$0" -o aotool 4 | exit 0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | char *vmmap() { 18 | char *cmd = NULL; 19 | asprintf(&cmd, "vmmap %d", getpid()); 20 | FILE *f = popen(cmd, "r"); 21 | size_t size = 10240; 22 | char *buf = malloc(size); 23 | ssize_t off = 0; 24 | char tmp[1025]; 25 | while (!feof(f) && !ferror(f)) { 26 | size_t n = fread(tmp, 1, sizeof(tmp), f); 27 | if (n == 0) { 28 | break; 29 | } 30 | if (n > size - off - 1) { 31 | size += 10240; 32 | char *nbuf = realloc(buf, size); 33 | if (!nbuf) { 34 | perror("realloc failed"); 35 | abort(); 36 | } 37 | buf = nbuf; 38 | } 39 | memcpy(&buf[off], tmp, n); 40 | off += n; 41 | buf[off] = '\0'; 42 | } 43 | fclose(f); 44 | return buf; 45 | } 46 | 47 | void dump_aot(const char *match, const char *out) { 48 | fprintf(stderr, "[+] Dumping AOT (%s -> %s)\n", match, out); 49 | char *match_aot = NULL; 50 | asprintf(&match_aot, "%s.aot", match); 51 | 52 | char *map = vmmap(); 53 | char *lasts = NULL; 54 | char *line = strtok_r(map, "\n", &map); 55 | 56 | FILE *f = fopen(out, "w"); 57 | while (line) { 58 | if (strstr(line, ".aot")) { 59 | uint64_t start, end; 60 | sscanf(line, "%*s %*s %llx-%llx", &start, &end); 61 | 62 | char *name = strstr(line, ".aot"); 63 | while (*name != ' ' && *name != '/') { name--; } 64 | name++; 65 | 66 | if (strcmp(basename(name), match_aot) == 0) { 67 | printf("%s\n", line); 68 | fwrite((uint8_t *)start, 1, end - start, f); 69 | } 70 | } 71 | line = strtok_r(map, "\n", &map); 72 | } 73 | fclose(f); 74 | } 75 | 76 | char *compile(const char *path) { 77 | fprintf(stderr, "[+] Compiling %s\n", path); 78 | char *name = NULL; 79 | pid_t pid = fork(); 80 | if (pid) { 81 | int status = 0; 82 | waitpid(pid, &status, 0); 83 | if (status != 0) { 84 | fprintf(stderr, "failed to compile: %d\n", status); 85 | } 86 | asprintf(&name, ".%d.dylib", pid); 87 | } else { 88 | asprintf(&name, ".%d.dylib", getpid()); 89 | unlink(name); 90 | execlp("gcc", "gcc", "-shared", "-arch", "x86_64", path, "-o", name, NULL); 91 | fprintf(stderr, "failed to exec compiler\n"); 92 | exit(2); 93 | } 94 | return name; 95 | } 96 | 97 | void disas(const char *path) { 98 | fprintf(stderr, "[+] Disassembling %s\n", path); 99 | pid_t pid = fork(); 100 | if (pid) { 101 | int status = 0; 102 | waitpid(pid, &status, 0); 103 | if (status != 0) { 104 | fprintf(stderr, "failed to disas: %d\n", status); 105 | } 106 | } else { 107 | execlp("otool", "otool", "-tv", path, NULL); 108 | fprintf(stderr, "failed to exec disassembler\n"); 109 | exit(2); 110 | } 111 | } 112 | 113 | void *loadlib(const char *path) { 114 | void *addr = dlopen(path, RTLD_NOW|RTLD_LOCAL); 115 | if (addr == NULL) { 116 | fprintf(stderr, "failed to load: %s\n", path); 117 | exit(2); 118 | } 119 | return addr; 120 | } 121 | 122 | void usage() { 123 | fprintf(stderr, "Usage: aotool -l \n"); 124 | fprintf(stderr, " aotool -c \n"); 125 | fprintf(stderr, " aotool -d \n"); 126 | exit(1); 127 | } 128 | 129 | int main(int argc, char **argv) { 130 | if (argc < 2) { usage(); } 131 | 132 | if (strcmp(argv[1], "-l") == 0) { 133 | if (argc != 4) { usage(); } 134 | loadlib(argv[2]); 135 | dump_aot(strdup(basename(argv[2])), argv[3]); 136 | 137 | } else if (strcmp(argv[1], "-c") == 0) { 138 | if (argc != 4) { usage(); } 139 | char *path = compile(argv[2]); 140 | void *addr = dlopen(path, RTLD_NOW|RTLD_LOCAL); 141 | unlink(path); 142 | if (addr == NULL) { 143 | fprintf(stderr, "failed to load: %s\n", path); 144 | exit(2); 145 | } 146 | dump_aot(strdup(basename(path)), argv[3]); 147 | 148 | } else if (strcmp(argv[1], "-d") == 0) { 149 | if (argc != 3) { usage(); } 150 | char *path = compile(argv[2]); 151 | loadlib(path); 152 | 153 | char *path_aot = NULL; 154 | asprintf(&path_aot, "%s.aot", path); 155 | dump_aot(path, path_aot); 156 | disas(path); 157 | disas(path_aot); 158 | unlink(path); 159 | unlink(path_aot); 160 | } else { 161 | usage(); 162 | } 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /utils/arctop/arctop.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Dict, List 3 | import argparse 4 | import sys 5 | import time 6 | 7 | DEFAULT_KEYS = [ 8 | 'hits', 9 | 'misses', 10 | 'size', 11 | 'l2_hits', 12 | 'l2_misses', 13 | 'l2_size', 14 | ] 15 | 16 | # suffixes / keys that are rendered as bytes 17 | BYTE_SUFFIXES = ('_bytes', '_asize', '_size', '_min', '_max') 18 | BYTE_KEYS = {'c', 'p', 'size'} 19 | 20 | @dataclass 21 | class Snapshot: 22 | ts: float 23 | stats: Dict 24 | 25 | def __getitem__(self, key: str) -> int: 26 | return self.stats[key] 27 | 28 | def __getattr__(self, key: str) -> int: 29 | try: return self.stats[key] 30 | except KeyError: raise AttributeError(key) 31 | 32 | def diff(a: Snapshot, b: Snapshot) -> Snapshot: 33 | diff = {key: b.stats[key] - a.stats[key] for key in b.stats} 34 | return Snapshot(b.ts - a.ts, diff) 35 | 36 | def unit_scale(number: float, byte_scale: bool=False, append: str=''): 37 | unit = 1000 38 | suffixes = ' kmbtq' 39 | if byte_scale: 40 | unit = 1024 41 | suffixes = 'BKMGTPEZY' 42 | 43 | acc = 1 44 | scales = [] 45 | for suffix in suffixes: 46 | scales.append((acc, suffix.strip())) 47 | acc *= unit 48 | 49 | for scale, suffix in reversed(scales): 50 | if abs(number) >= scale: 51 | return f"{number / scale:.2f}{suffix}{append}" 52 | return f"{number:.2f}{append}" 53 | 54 | class Stats: 55 | history: List[Snapshot] 56 | first: Snapshot 57 | 58 | def __init__(self): 59 | self.history = [] 60 | self.first = self.update() 61 | 62 | def update(self) -> Snapshot: 63 | now = time.perf_counter() 64 | stats = {} 65 | with open('/proc/spl/kstat/zfs/arcstats', 'r') as f: 66 | data = f.read().strip() 67 | for line in data.split('\n')[2:]: 68 | name, _, value = line.split() 69 | stats[name] = int(value) 70 | 71 | snapshot = Snapshot(now, stats) 72 | self.history.append(snapshot) 73 | self.history = self.history[-300:] 74 | return snapshot 75 | 76 | def display(self, a: Snapshot, b: Snapshot, fields: List[str]): 77 | elapsed = diff(self.first, b) 78 | interval = diff(a, b) 79 | rows = [] 80 | rows.append(( 81 | 'cat', 82 | 'key', 83 | 'raw', 84 | 'abs', 85 | 'run', 86 | 'run/s', 87 | '|', 88 | 'int', 89 | 'int/s', 90 | )) 91 | if not fields: 92 | fields = interval.stats.keys() 93 | for key in fields: 94 | byte_scale = (key in BYTE_KEYS or key.endswith(BYTE_SUFFIXES)) 95 | if '_' in key: 96 | cat, keyname = key.split('_', 1) 97 | else: 98 | cat, keyname = '', key 99 | 100 | avalue = b[key] 101 | evalue = elapsed[key] 102 | ivalue = interval[key] 103 | eps = evalue / (elapsed.ts + 1e-9) 104 | ips = ivalue / (interval.ts + 1e-9) 105 | rows.append(( 106 | cat, 107 | keyname, 108 | str(avalue), 109 | unit_scale(avalue, byte_scale=byte_scale, append='' ), 110 | unit_scale(evalue, byte_scale=byte_scale, append='' ), 111 | unit_scale(eps, byte_scale=byte_scale, append='/s'), 112 | '|', 113 | unit_scale(ivalue, byte_scale=byte_scale, append='' ), 114 | unit_scale(ips, byte_scale=byte_scale, append='/s'), 115 | )) 116 | 117 | col_pad = [len(max(col, key=lambda x: len(x))) for col in zip(*rows)] 118 | sys.stdout.write('\033[H') 119 | sys.stdout.flush() 120 | for i, row in enumerate(rows): 121 | print('\033[K' + (' '.join([ 122 | col.rjust(pad) 123 | for j, (col, pad) in enumerate(zip(row, col_pad)) 124 | ]))) 125 | sys.stdout.write('\033[0J') 126 | sys.stdout.flush() 127 | 128 | def top(self, interval: float, fields=None): 129 | if fields is None: 130 | fields = DEFAULT_KEYS 131 | last_snapshot = self.update() 132 | self.display(last_snapshot, last_snapshot, fields) 133 | while True: 134 | time.sleep(1) 135 | snapshot = self.update() 136 | if snapshot.ts - last_snapshot.ts > interval: 137 | self.display(last_snapshot, snapshot, fields) 138 | last_snapshot = snapshot 139 | 140 | if __name__ == '__main__': 141 | parser = argparse.ArgumentParser() 142 | parser.add_argument('-d', help='interval', type=int, default=5) 143 | parser.add_argument('-a', help='print all fields', action='store_true') 144 | parser.add_argument('-f', help='fields', type=str, default=','.join(DEFAULT_KEYS)) 145 | args = parser.parse_args() 146 | 147 | stats = Stats() 148 | stats.top(interval=args.d, fields=args.f.split(',') if not args.a else ()) 149 | -------------------------------------------------------------------------------- /utils/retina/retina.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | uint32_t mode; 11 | uint32_t flags; 12 | uint32_t width; 13 | uint32_t height; 14 | uint32_t depth; 15 | uint32_t dc2[42]; 16 | uint16_t dc3; 17 | uint16_t freq; 18 | uint32_t dc4[4]; 19 | float scale; 20 | 21 | char name[64]; 22 | int skip; 23 | } display_mode_t; 24 | 25 | #define MODE_SIZE (sizeof(display_mode_t) - sizeof(char) * 32 - sizeof(int)) 26 | void CGSGetCurrentDisplayMode(CGDirectDisplayID display, int *mode); 27 | void CGSConfigureDisplayMode(CGDisplayConfigRef config, CGDirectDisplayID display, int mode); 28 | void CGSGetNumberOfDisplayModes(CGDirectDisplayID display, int *count); 29 | void CGSGetDisplayModeDescriptionOfLength(CGDirectDisplayID display, int index, display_mode_t *mode, int length); 30 | 31 | // sorts for highest effective resolution modes first 32 | int sort_modes(const void *a, const void *b) { 33 | const display_mode_t *da = a, *db = b; 34 | if (strlen(da->name) < strlen(db->name) || da->scale > db->scale) return 1; 35 | if (strlen(da->name) > strlen(db->name) || da->scale < db->scale) return -1; 36 | return strcmp(da->name, db->name) * -1; 37 | } 38 | 39 | // grab all the modes and attach a name string 40 | void get_all_modes(CGDirectDisplayID display, display_mode_t **retModes, int *count) { 41 | CGSGetNumberOfDisplayModes(display, count); 42 | if (! *count || !retModes) return; 43 | display_mode_t *modes = malloc(sizeof(display_mode_t) * *count); 44 | for (int i = 0; i < *count; i++) { 45 | CGSGetDisplayModeDescriptionOfLength(display, i, modes+i, MODE_SIZE); 46 | display_mode_t *mode = &modes[i]; 47 | 48 | off_t off = 0; 49 | #define add(...) off += snprintf(mode->name + off, 64 - off, __VA_ARGS__) 50 | add("%dx%d", mode->width, mode->height); 51 | if (mode->scale > 1) 52 | add("@%.0f", mode->scale); 53 | if (mode->freq > 0) 54 | add(",%hu", mode->freq); 55 | uint32_t depth = mode->depth * 8; 56 | if (depth != 32) { 57 | add(",%d-bit", depth); 58 | } 59 | #undef cat 60 | } 61 | qsort(modes, *count, sizeof(display_mode_t), sort_modes); 62 | *retModes = modes; 63 | } 64 | 65 | // get the current mode for a display 66 | int get_display_mode(CGDirectDisplayID display) { 67 | int mode; 68 | CGSGetCurrentDisplayMode(display, &mode); 69 | return mode; 70 | } 71 | 72 | // set the current mode for a display 73 | void set_display_mode(CGDirectDisplayID display, int mode) { 74 | CGDisplayConfigRef config; 75 | CGBeginDisplayConfiguration(&config); 76 | CGSConfigureDisplayMode(config, display, mode); 77 | CGCompleteDisplayConfiguration(config, kCGConfigurePermanently); 78 | } 79 | 80 | void print_display(CGDirectDisplayID display, int num) { 81 | display_mode_t *modes; 82 | int count; 83 | get_all_modes(display, &modes, &count); 84 | 85 | int current_mode_num = get_display_mode(display); 86 | display_mode_t *current_mode; 87 | for (int i = 0; i < count; i++) { 88 | if (modes[i].mode == current_mode_num) { 89 | current_mode = &modes[i]; 90 | } 91 | } 92 | 93 | printf("Display [%d]", num); 94 | if (current_mode != NULL) { 95 | printf(" (now: %s)\n", current_mode->name); 96 | } else { 97 | printf("\n"); 98 | } 99 | printf(" Allowed modes:\n "); 100 | int pad = 0; 101 | for (int i = 0; i < count; i++) { 102 | modes[i].skip = 0; 103 | int len = strlen(modes[i].name); 104 | if (len > pad) pad = len; 105 | } 106 | char fmt[32] = {0}; 107 | snprintf(fmt, 32, "%%%ds", pad + 2); 108 | for (int i = 0; i < count; i++) { 109 | display_mode_t *a = &modes[i], *b; 110 | if (a->skip) continue; 111 | a->skip = 1; 112 | // pad to column * scale (in case a resolution isn't available unscaled?) 113 | for (int s = 1; s < a->scale; s++) 114 | printf(fmt, ""); 115 | printf(fmt, a->name); 116 | // print scaled equivalents in the same row 117 | for (int j = 0; j < count; j++) { 118 | b = &modes[j]; 119 | if (a == b || b->skip) continue; 120 | if (a->width * a->scale == b->width * b->scale && 121 | a->height * a->scale == b->height * b->scale) { 122 | printf(fmt, b->name); 123 | b->skip = 1; 124 | } 125 | } 126 | printf("\n "); 127 | } 128 | printf("\n"); 129 | } 130 | 131 | void set_friendly_mode(CGDirectDisplayID display, int num, const char *mode) { 132 | display_mode_t *modes; 133 | int count; 134 | get_all_modes(display, &modes, &count); 135 | int current_mode_num = get_display_mode(display); 136 | 137 | for (int i = 0; i < count; i++) { 138 | if (strcmp(modes[i].name, mode) == 0) { 139 | if (modes[i].mode == current_mode_num) { 140 | printf("[%d] already at %s\n", num, mode); 141 | return; 142 | } 143 | set_display_mode(display, modes[i].mode); 144 | return; 145 | } 146 | } 147 | printf("[%d] does not support mode %s\n", num, mode); 148 | print_display(display, num); 149 | } 150 | 151 | int main(int argc, const char **argv) { 152 | CGDirectDisplayID activeDisplays[128]; 153 | uint32_t count; 154 | CGGetActiveDisplayList(128, activeDisplays, &count); 155 | if (argc < 2) { 156 | printf("Usage: %s [display] \n", argv[0]); 157 | for (int i = 0; i < count; i++) { 158 | print_display(activeDisplays[i], i); 159 | } 160 | return 1; 161 | } else if (argc == 2) { 162 | set_friendly_mode(CGMainDisplayID(), 0, argv[1]); 163 | } else if (argc > 2) { 164 | for (int i = 1; i < argc; i += 2) { 165 | int display = strtol(argv[i], NULL, 10); 166 | if (display >= 0 && display < count && i + 1 < argc) { 167 | set_friendly_mode(activeDisplays[display], display, argv[i + 1]); 168 | } else { 169 | printf("Invalid final display arguments (at %s).\n", argv[i]); 170 | } 171 | } 172 | } 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /snippets/python/threadpool.py: -------------------------------------------------------------------------------- 1 | from Queue import Queue, Empty 2 | import threading 3 | import traceback 4 | import inspect 5 | import os, stat 6 | 7 | def isiter(obj): 8 | ''' 9 | test whether an object conforms to the iterator protocol 10 | ''' 11 | try: 12 | if obj.__iter__() == obj and obj.next: 13 | return True 14 | except AttributeError: 15 | pass 16 | 17 | return False 18 | 19 | class stack_size: 20 | def __init__(self, stack_size): 21 | self.stack_size = stack_size 22 | self.old_size = threading.stack_size() 23 | 24 | def __enter__(self): 25 | threading.stack_size(self.old_size) 26 | 27 | def __exit__(self, type, value, traceback): 28 | threading.stack_size(self.stack_size) 29 | 30 | class Lock: 31 | def __init__(self): 32 | self.lock = threading.Lock() 33 | 34 | def __enter__(self): 35 | self.lock.acquire() 36 | 37 | def __exit__(self, type, value, traceback): 38 | self.lock.release() 39 | 40 | class FilenameLogger: 41 | def __init__(self, filename): 42 | self.filename = filename 43 | self.filepath = os.path.join(os.getcwd(), filename) 44 | self.fileobj = open(self.filepath, 'a') 45 | 46 | def __call__(self, msg): 47 | # someone renamed our file! maybe logrotate'd. open a new one. 48 | if not os.path.exists(self.filepath) or os.stat(self.filepath)[stat.ST_INO] != os.fstat(self.fileobj.fileno())[stat.ST_INO]: 49 | self.fileobj = open(self.filepath, 'a') 50 | 51 | self.fileobj.write(msg+'\n') 52 | 53 | class FileObjLogger: 54 | def __init__(self, fileobj=None, filename=None): 55 | self.fileobj = fileobj or open(filename, 'a') if filename else None 56 | 57 | def __call__(self, msg): 58 | if self.fileobj and not self.fileobj.closed: 59 | self.fileobj.write(msg+'\n') 60 | 61 | class ThreadSafeLogger: 62 | ''' 63 | Init with a list of loggers to call when this class is called() 64 | "None" logs to stdout, string obj logs to filename, fileobj logs to write(), callable(obj) acts as a callback. 65 | 66 | formatter=callback allows you to set a callback which can rewrite the message passed to log 67 | separator=str allows you to set the separator between args passed 68 | ''' 69 | def __init__(self, *args, **kwargs): 70 | self.formatter = None 71 | self.separator = ', ' 72 | if 'formatter' in kwargs and callable(kwargs['formatter']): 73 | self.formatter = kwargs['formatter'] 74 | if 'separator' in kwargs: 75 | self.separator = kwargs['separator'] 76 | 77 | loggers = set() 78 | for logger in args: 79 | if hasattr(logger, 'write') and callable(logger.write): 80 | loggers.add(FileObjLogger(fileobj=logger)) 81 | elif isinstance(logger, basestring): 82 | loggers.add(FilenameLogger(logger)) 83 | elif callable(logger): 84 | loggers.add(logger) 85 | else: 86 | loggers.add(self.echo) 87 | 88 | self.loggers = loggers or (self.echo,) 89 | self.lock = Lock() 90 | 91 | def __call__(self, *args): 92 | with self.lock: 93 | self.callback(*args) 94 | 95 | def callback(self, *args): 96 | msg = self.separator.join(str(arg) for arg in args) 97 | if self.formatter: 98 | msg = self.formatter(msg) 99 | 100 | for logger in self.loggers: 101 | logger(msg) 102 | 103 | def echo(self, msg): 104 | print msg 105 | 106 | class ThreadPool: 107 | def __init__(self, max_threads, log_returns=False, catch_returns=False, logger=None, stack_size=0, return_queue=1000): 108 | self.lock = threading.Lock() 109 | self.max = max_threads 110 | self.logger = logger or (lambda *x: None) 111 | self.stack_size = stack_size 112 | self.log_returns = log_returns 113 | self.catch_returns = catch_returns 114 | 115 | self.call_queue = Queue() 116 | self.returns = Queue(return_queue) 117 | self.spawn_workers() 118 | 119 | def __call__(self, func): 120 | def wrapper(*args, **kwargs): 121 | self.call_queue.put((func, args, kwargs)) 122 | 123 | return wrapper 124 | 125 | def spawn_workers(self): 126 | for i in xrange(self.max): 127 | thread = threading.Thread(target=self.worker, args=(self.call_queue,)) 128 | thread.daemon = True 129 | thread.start() 130 | 131 | def worker(self, call): 132 | while True: 133 | func, args, kwargs = call.get() 134 | try: 135 | result = func(*args, **kwargs) 136 | if self.catch_returns or self.log_returns: 137 | if inspect.isgenerator(result) or isiter(result): 138 | for x in result: 139 | self.returned(x) 140 | else: 141 | self.returned(result) 142 | except: 143 | self.logger(traceback.format_exc()) 144 | finally: 145 | call.task_done() 146 | 147 | def returned(self, result): 148 | if self.log_returns: 149 | self.logger(result) 150 | if self.catch_returns: 151 | self.returns.put(result) 152 | 153 | def pop(self): 154 | ''' 155 | pop a result from the queue, blocks if we have none 156 | ''' 157 | if self.catch_returns: 158 | result = self.returns.get() 159 | self.returns.task_done() 160 | return result 161 | 162 | def iter(self): 163 | ''' 164 | act as a generator, returning results as they happen 165 | this method assumes you've already queued all of your calls 166 | ''' 167 | if not self.catch_returns: 168 | raise Exception 169 | 170 | while self.call_queue.unfinished_tasks > 0: 171 | try: 172 | yield self.returns.get(timeout=0.1) 173 | except Empty: 174 | pass 175 | 176 | for value in self.finish(): 177 | yield value 178 | 179 | def flush(self): 180 | ''' 181 | clear and return the function returns queue 182 | ''' 183 | if self.catch_returns: 184 | results = tuple(self.returns.queue) 185 | self.returns = Queue() 186 | return results 187 | 188 | return () 189 | 190 | def finish(self): 191 | ''' 192 | wait for queue to finish, then return flush() 193 | ''' 194 | self.call_queue.join() 195 | return self.flush() 196 | 197 | if __name__ == '__main__': 198 | log = ThreadSafeLogger() 199 | # don't use catch_returns unless you're going to use them - otherwise they will never clear and just leak memory 200 | pool = ThreadPool(100, logger=log, log_returns=True, catch_returns=True) 201 | 202 | import time 203 | 204 | @pool 205 | def test(i): 206 | log('from thread', i) 207 | yield 'yield %i' % i 208 | # sleep to show how awesome it is that we allow generators 209 | time.sleep(0.5) 210 | yield 'yield %i' % (i*10) 211 | 212 | for i in xrange(1,6): 213 | test(i) 214 | 215 | # because these pops will all happen before the sleep is finished, we'll get numbers < 100 216 | log('first pop') 217 | for i in xrange(5): 218 | log(pool.pop()) 219 | 220 | # just to make sure it works 221 | pool.flush() 222 | 223 | # because these pops will happen after the sleep, we'll get numbers over 100 224 | results = pool.finish() 225 | print results 226 | -------------------------------------------------------------------------------- /utils/jaraudit/jaraudit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from collections import defaultdict 3 | from xml.dom.minidom import parse as parseXML 4 | import fnmatch 5 | import json 6 | import os 7 | import re 8 | import sys 9 | import zipfile 10 | 11 | try: 12 | import yaml 13 | except ImportError: 14 | print "PyYAML not found." 15 | print "`pip install pyyaml`" 16 | print 17 | print "If you don't have pip:" 18 | print "Grab a tarball: https://pypi.python.org/pypi/PyYAML" 19 | print " tar -xf PyYAML*.tar.gz" 20 | print " mv PyYAML*/lib/yaml ." 21 | sys.exit(1) 22 | 23 | def find(base, *patterns): 24 | for root, dirs, files in os.walk(base): 25 | for name in files: 26 | for pattern in patterns: 27 | if fnmatch.fnmatch(name, pattern): 28 | yield os.path.join(root, name) 29 | 30 | def name_match(a, b): 31 | if a == b or a in b: 32 | return True 33 | return False 34 | 35 | class VersionMatcher: 36 | def __init__(self, versions): 37 | self.raw = versions 38 | self.criteria = [ 39 | [self.split(v) for v in version.split(',')] 40 | for version in versions 41 | ] 42 | 43 | @staticmethod 44 | def split(v): 45 | sign = '=' 46 | m = re.match('^([><]=?)(.*?)$', v) 47 | if m: 48 | sign, v = m.groups() 49 | out = [] 50 | for sub in v.split('.'): 51 | if sub.isdigit(): 52 | out.append(int(sub)) 53 | else: 54 | out.append(sub) 55 | return sign, tuple(out) 56 | 57 | @staticmethod 58 | def satisfies(criteria, version): 59 | signs = { 60 | '=': lambda a, b: a[:len(b)] == b[:len(a)], 61 | '<': lambda a, b: a < b, 62 | '>': lambda a, b: a > b, 63 | '<=': lambda a, b: a <= b, 64 | '>=': lambda a, b: a >= b, 65 | } 66 | for sign, match in criteria: 67 | if not signs[sign](version, match): 68 | return False 69 | return True 70 | 71 | def match(self, version): 72 | _, version = self.split(version) 73 | for c in self.criteria: 74 | if self.satisfies(c, version): 75 | return True 76 | return False 77 | 78 | def __repr__(self): 79 | return "".format(self.raw) 80 | 81 | def version_match(v, artifact): 82 | version = VersionMatcher(artifact.get('version', [])) 83 | fixedin = VersionMatcher(artifact.get('fixedin', [])) 84 | if version.match(v) and not fixedin.match(v): 85 | return True 86 | return False 87 | 88 | first_dedup = set() 89 | 90 | def check(db, package, version): 91 | global first_dedup 92 | out = [] 93 | if not package.strip(): 94 | return out 95 | for artifact in db['affected']: 96 | _id = artifact['artifactId'] 97 | if name_match(package, _id): 98 | if version_match(version, artifact): 99 | out.append({ 100 | 'package': package, 101 | 'version': version, 102 | 'db': db, 103 | }) 104 | if ((package, version)) in first_dedup: 105 | continue 106 | first_dedup.add((package, version)) 107 | print >>sys.stderr, 'Found:', package, version, 'CVE-' + db['cve'], 108 | if package != _id: 109 | print >>sys.stderr, '(WARNING: fuzzy "%s" != "%s")' % (package, _id), 110 | print >>sys.stderr 111 | return out 112 | 113 | def load(yml): 114 | with open(yml, 'r') as f: 115 | return yaml.load(f) 116 | 117 | def search(paths): 118 | for path in paths: 119 | for match in find(path, '*.jar', 'pom.xml'): 120 | try: 121 | if os.path.basename(match) == 'pom.xml': 122 | x = parseXML(match) 123 | deps = x.getElementsByTagName('dependency') 124 | for d in deps: 125 | artifact = d.getElementsByTagName('artifactId')[0].childNodes[0].data 126 | version = d.getElementsByTagName('version') 127 | if version: 128 | try: 129 | version = version[0].childNodes[0].data 130 | m = re.match(r'^\$\{(.+)\}$', version) 131 | if m: 132 | lookup = m.group(1) 133 | version = x.getElementsByTagName(lookup)[0].childNodes[0].data 134 | except Exception: 135 | continue 136 | yield match, artifact, version 137 | continue 138 | 139 | filename = os.path.splitext(os.path.basename(match))[0].strip() 140 | version = None 141 | try: 142 | zf = zipfile.ZipFile(match, 'r') 143 | try: 144 | f = zf.open('META-INF/MANIFEST.MF') 145 | contents = f.read() 146 | except KeyError: 147 | continue 148 | finally: 149 | zf.close() 150 | if 'Implementation-Version: ' in contents: 151 | version = contents.split('Implementation-Version: ', 1)[1].split('\n', 1)[0].strip() 152 | except zipfile.BadZipfile: 153 | version = filename.split('-', 1)[1] 154 | 155 | if version is not None: 156 | if '"' in version: 157 | version = version.split('"', 3)[1] 158 | version = version.split(' ', 1)[0].split('+', 1)[0] 159 | name = filename.split(version, 1)[0].split('_', 1)[0].rstrip('-_.') 160 | yield match, name, version 161 | except Exception: 162 | import traceback 163 | traceback.print_exc() 164 | 165 | def shellquote(s): 166 | return "'%s'" % s.replace('\\', '\\\\').replace("'", "\\'") 167 | 168 | def usage(): 169 | print 'Usage: %s [-j,--json] path [path...]' % os.path.basename(sys.argv[0]) 170 | sys.exit(1) 171 | 172 | if __name__ == '__main__': 173 | base = os.path.dirname(str(__file__)) 174 | if base == '.': 175 | base = '' 176 | 177 | language = 'java' 178 | db = os.path.join(base, 'victims-cve-db', 'database', language) 179 | if not os.path.exists(db): 180 | print "victims-cve-db not found." 181 | print 182 | if base: 183 | print "pushd %s" % shellquote(base) 184 | print "wget https://github.com/victims/victims-cve-db/archive/master.tar.gz" 185 | print "tar -xf master.tar.gz; mv victims-cve-db-master victims-cve-db" 186 | if base: 187 | print "popd" 188 | sys.exit(1) 189 | 190 | if len(sys.argv) < 2: 191 | usage() 192 | 193 | j = False 194 | if sys.argv[1] in ('-j', '--json'): 195 | j = True 196 | if len(sys.argv) < 3: 197 | usage() 198 | 199 | dbs = list([load(y) for y in find(db, '*.yaml')]) 200 | first = True 201 | 202 | results = [] 203 | file_count = 0 204 | for path, name, version in search((sys.argv[1:]) or ('.',)): 205 | file_count += 1 206 | for db in dbs: 207 | result = check(db, name, version) 208 | for r in result: 209 | r['path'] = path 210 | results.extend(result) 211 | 212 | out = defaultdict(list) 213 | for r in results: 214 | out[r['package']].append(r) 215 | 216 | if out: 217 | print >>sys.stderr 218 | for key, items in sorted(out.items()): 219 | dedup = set() 220 | if j: 221 | print json.dumps(items) 222 | else: 223 | first = items[0] 224 | print 'Package:', first['package'] 225 | print 'Version:', first['version'] 226 | print 'Path: ', first['path'] 227 | for item in items: 228 | db = item['db'] 229 | if db['cve'] in dedup: 230 | continue 231 | dedup.add(db['cve']) 232 | print '- Title:', db['title'] 233 | print ' CVE:', 'CVE-' + db['cve'] 234 | print ' URL:', db['references'][0] 235 | print 236 | print >>sys.stderr, '(%d) files found. (%d) vulnerable.' % (file_count, len(out)) 237 | -------------------------------------------------------------------------------- /utils/tvsort/tvsort.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # tv_sort.py 3 | # detects and sorts tv shows automatically based on filename 4 | 5 | import os 6 | import shutil 7 | 8 | import re 9 | import xmlrpclib 10 | 11 | words = r'([\'\w\.\- ]+?)' 12 | spacers = re.compile(r'[\s_\-\.]+') 13 | show_filename = re.compile(words + r'S(\d+)\s?EP?(\d+)', re.IGNORECASE) 14 | show_filename_2 = re.compile(words + r'(\d{4}\.[012]?[0-9]\.\d{1,2})()', re.IGNORECASE) 15 | show_filename_3 = re.compile(words + r'(\d+)x(\d+)', re.IGNORECASE) 16 | show_filename_4 = re.compile(words + r'(\d+?)(\d{1,2})', re.IGNORECASE) 17 | word_match = re.compile('(%s+)' % words, re.IGNORECASE) 18 | the_match = re.compile(r'(.*?)(, The)$', re.IGNORECASE) 19 | hd_match = re.compile(r'(720p|1080p)') 20 | 21 | date = r'(19\d{2}|2\d{3})' 22 | date_match = re.compile(r' ?(\(%s\)|%s) ?' % (date, date)) 23 | 24 | extensions = ('.mp4', '.avi', '.mkv', '.m4v', '.wmv', '.mpg', '.mpeg') 25 | 26 | def forward_the(name): 27 | if the_match.match(name): 28 | return the_match.sub(r'The \1', name) 29 | return name 30 | 31 | class Auto(dict): 32 | def __init__(self, path): 33 | self.path = path 34 | if os.path.isfile(path): 35 | f = open(path, 'r') 36 | for line in f.readlines(): 37 | line = line.strip() 38 | if line and ':' in line: 39 | key, value = line.split(':', 1) 40 | key, value = key.strip(), value.strip() 41 | value = value.strip('\'"') 42 | self[key] = value 43 | 44 | class Folder: 45 | def __init__(self, path): 46 | self.path = path 47 | self.name = os.path.split(path)[1] 48 | self.regex = None 49 | self.naming = 'Season %(season)s/%(name)s %(epstr)s' 50 | 51 | self.auto = Auto(os.path.join(path, '.auto')) 52 | if 'regex' in self.auto: 53 | try: 54 | self.regex = re.compile(self.auto['regex'], re.IGNORECASE) 55 | except: 56 | print 'error parsing regex for: %s' % self.auto.path 57 | if 'name' in self.auto: 58 | self.naming = self.auto['name'] 59 | 60 | if not self.regex: 61 | # TODO: this is terrible for single-letter names 62 | text = word_match.match(self.name).group() 63 | text = spacers.sub(' ', text) 64 | self.regex = re.compile(re.escape(text), re.IGNORECASE) 65 | 66 | def match(self, name): 67 | match = self.regex.search(name) 68 | if match: 69 | return True 70 | 71 | return False 72 | 73 | def rename(self, name, season, episode, hd=None, ext='.avi'): 74 | if season and episode: 75 | season = int(season) 76 | episode = int(episode) 77 | epstr = 'S%02dE%02d' % (season, episode) 78 | else: 79 | epstr = season 80 | if not self.auto: 81 | self.naming = '%(name)s %(epstr)s' 82 | target = self.naming % {'name':name, 'season':season, 'episode':episode, 'epstr':epstr} 83 | if hd: 84 | target = '%s (%s)' % (target, hd) 85 | 86 | return self.path, target + ext, epstr 87 | 88 | def __repr__(self): 89 | return '' % self.name 90 | 91 | def extract(path): 92 | cwd = os.getcwd() 93 | os.chdir(path) 94 | first = os.listdir('.') 95 | for entry in first: 96 | if entry.endswith('.rar'): 97 | print 'Extracting: %s' % entry, 98 | os.popen('unrar x -o- %s' % entry) 99 | break 100 | 101 | second = os.listdir('.') 102 | for entry in second: 103 | ext = os.path.splitext(entry)[1] 104 | if ext in extensions: 105 | print '=> "%s"' % entry 106 | os.chdir(cwd) 107 | return entry, ext 108 | else: 109 | print '- Nothing found :(' 110 | os.chdir(cwd) 111 | return None, None 112 | 113 | def move(path, filename, name, season, episode, folders, force_move=False, dry_run=False, link=False, hd=None, ext='.avi'): 114 | copy = True 115 | 116 | for folder in folders: 117 | if folder.match(name): 118 | break 119 | else: 120 | print 'Skipped "%s" (example: %s)' % (name, filename) 121 | return True 122 | 123 | target_folder, target, epstr = folder.rename(name, season, episode, hd, ext) 124 | 125 | no_ext = os.path.splitext(os.path.join(target_folder, target))[0] 126 | test_base = os.path.split(no_ext)[0] 127 | for test in extensions: 128 | test_path = no_ext + test 129 | test_filename = os.path.split(test_path)[1] 130 | 131 | if not os.path.exists(test_base): 132 | if not dry_run: 133 | os.makedirs(test_base) 134 | 135 | break 136 | else: 137 | if test_base == path: 138 | if test_filename == filename: 139 | return 140 | else: 141 | for existing in os.listdir(test_base): 142 | if existing.lower() == test_filename.lower(): 143 | return 144 | 145 | path = os.path.join(path, filename) 146 | if not os.path.exists(path): 147 | return 148 | if os.path.isdir(path) and not dry_run: 149 | entry, ext = extract(path) 150 | if not entry: 151 | print 'no extracted file found.' 152 | return 153 | else: 154 | print 'success.' 155 | 156 | path = os.path.join(path, entry) 157 | copy = False 158 | 159 | target_folder, target, epstr = folder.rename(name, season, episode, hd, ext) 160 | target_path, target_filename = os.path.split(os.path.join(target_folder, target)) 161 | print ('%s %s => %s' % (name, epstr, target)), 162 | 163 | target = os.path.join(target_folder, target) 164 | if dry_run: 165 | verb = copy and 'copy' or 'move' 166 | print 'Would %s %s => %s' % (verb, path, target) 167 | else: 168 | if copy and not force_move: 169 | if link: 170 | try: 171 | os.link(path, target) 172 | print 'linked to target' 173 | except IOError: 174 | link = False 175 | 176 | if not link: 177 | shutil.copyfile(path, target) 178 | print 'copied to target.' 179 | else: 180 | os.rename(path, target) 181 | print 'moved to target.' 182 | 183 | def run(source, targets, force_move=False, dry_run=False, link=False): 184 | print 185 | print 'If anything is skipped, make sure the show name exists as a folder in one of the targets' 186 | print 'You can also use .auto files (see the readme) to change the show matching a folder' 187 | folders = [] 188 | for target in targets: 189 | if not os.path.isdir(target): continue 190 | for folder in os.listdir(target): 191 | path = os.path.join(target, folder) 192 | if not os.path.isdir(path): continue 193 | 194 | folders.append(Folder(path)) 195 | 196 | files = set() 197 | if source.startswith('rtorrent://'): 198 | rtorrent_uri = source.replace('rtorrent', 'http') 199 | x = xmlrpclib.ServerProxy(rtorrent_uri) 200 | seeding = x.download_list() 201 | 202 | for torrent in seeding: 203 | if not x.d.complete(torrent): continue 204 | files.add(x.d.get_base_path(torrent)) 205 | else: 206 | for filename in os.listdir(source): 207 | files.add(os.path.join(source, filename)) 208 | 209 | skip = set() 210 | for path in sorted(files): 211 | path, filename = os.path.split(path) 212 | 213 | cleaned_filename = hd_match.sub('', filename) 214 | match1 = show_filename.match(cleaned_filename) 215 | match2 = show_filename_2.match(cleaned_filename) 216 | match3 = show_filename_3.match(cleaned_filename) 217 | match4 = show_filename_4.match(cleaned_filename) 218 | hd = hd_match.search(filename) 219 | if hd: 220 | hd = hd.group() 221 | 222 | matches = [m for m in (match1, match2, match3, match4) if m] 223 | 224 | if matches: 225 | match = matches[0] 226 | name, season, episode = match.groups() 227 | 228 | # replace all spacer chars with spaces 229 | name = spacers.sub(' ', name) 230 | # strip the year from the name 231 | name = date_match.sub(' ', name) 232 | name = name.strip() 233 | first = name[0] 234 | if first == first.lower(): 235 | name = ' '.join(word.capitalize() for word in name.split(' ')) 236 | 237 | if name in skip: continue 238 | if name.endswith(' S'): 239 | # work around a bug when encountering full season downloads 240 | continue 241 | 242 | skip_name = move(path, filename, name, season, episode, folders, force_move, dry_run, link, hd) 243 | if skip_name: 244 | skip.add(name) 245 | 246 | def usage(): 247 | print 'Usage: ./tv_sort.py [flags] [target folder] [target folder]...' 248 | print 'Sorts TV shows into folders.' 249 | print ' Flags:' 250 | print ' -m, --move: force moving of normal files (extracted files are always moved)' 251 | print ' -l, --link: hardlink files instead of copying' 252 | print ' -d, --dry: dry run - only display what would be moved/copied' 253 | print ' Source options:' 254 | print ' * /path/to/folder' 255 | print ' * rtorrent://localhost/scgi_path' 256 | sys.exit(1) 257 | 258 | if __name__ == '__main__': 259 | import sys 260 | 261 | args = [] 262 | force_move = False 263 | dry_run = False 264 | link = False 265 | for arg in sys.argv[1:]: 266 | sort = None 267 | if arg.startswith('-'): 268 | sort = ''.join(sorted(arg)) 269 | 270 | if arg in ('-m', '--move'): 271 | force_move = True 272 | elif arg in ('-d', '--dry'): 273 | dry_run = True 274 | elif arg in ('-l', '--link'): 275 | link = True 276 | elif sort: 277 | if re.match(r'-[mdv]{1,3}', sort): 278 | if 'm' in sort: force_move = True 279 | if 'd' in sort: dry_run = True 280 | if 'l' in sort: link = True 281 | else: 282 | args.append(arg) 283 | 284 | if len(args) < 2: 285 | usage() 286 | else: 287 | run(args[0], args[1:], force_move=force_move, dry_run=dry_run, link=link) 288 | -------------------------------------------------------------------------------- /utils/snakedisk/snakedisk.py: -------------------------------------------------------------------------------- 1 | # requires-python = ">=3.14t" 2 | # dependencies = [ 3 | # "tqdm", 4 | # "git+https://github.com/lunixbochs/shishua-python.git", 5 | # ] 6 | 7 | from concurrent.futures import ThreadPoolExecutor 8 | from dataclasses import asdict, dataclass 9 | from pathlib import Path 10 | from shishua import SHISHUA 11 | from tqdm import tqdm 12 | from typing import Callable 13 | import argparse 14 | import functools 15 | import json 16 | import logging 17 | import os 18 | import random 19 | import subprocess 20 | import sys 21 | import time 22 | import traceback 23 | 24 | @dataclass 25 | class Device: 26 | path: str 27 | size: int 28 | limit: int 29 | read: int 30 | wrote: int 31 | errors: int 32 | seed: str 33 | state: str # reading | writing | done 34 | serial: str = "" 35 | found: bool = False 36 | 37 | @dataclass 38 | class State: 39 | pbar: tqdm 40 | blocksize: int 41 | limit: int 42 | seeksize: int 43 | devices: dict[str, Device] 44 | save_path: Path | None 45 | errors: int 46 | 47 | class SeekSHUA: 48 | rng: SHISHUA 49 | 50 | def __init__(self, *, seed: str, chunksize: int, pos: int = 0): 51 | self.seed = seed 52 | self.chunksize = chunksize 53 | self.pos = pos 54 | self.reseed() 55 | 56 | def reseed(self) -> None: 57 | chunk = self.pos // self.chunksize 58 | self.rng = SHISHUA(f"{self.seed}|chunk:{chunk}") 59 | 60 | chunkoff = self.pos % self.chunksize 61 | buf = bytearray(1 * 1024 * 1024) 62 | for i in range(0, chunkoff, len(buf)): 63 | need = min(len(buf), chunkoff - i) 64 | self.rng.fill(buf[:need]) # slicing here copies, but we're throwing buf away anyway 65 | 66 | def fill(self, buf: bytearray) -> None: 67 | mbuf = memoryview(buf) 68 | pos = 0 69 | need = len(buf) 70 | while need: 71 | feed = min(need, self.chunksize - self.pos % self.chunksize) 72 | self.rng.fill(mbuf[pos:pos + feed]) 73 | need -= feed 74 | pos += feed 75 | self.pos += feed 76 | if self.pos % self.chunksize == 0: 77 | self.reseed() 78 | 79 | def wrap_exc[T: Callable](fn: T) -> T: 80 | @functools.wraps(fn) 81 | def wrapper(*args, **kwargs): 82 | try: 83 | return fn(*args, **kwargs) 84 | except Exception: 85 | traceback.print_exc() 86 | raise 87 | return wrapper 88 | 89 | @functools.cache 90 | def read_path_to_serial() -> dict[str, str]: 91 | try: 92 | p = subprocess.run(["lsblk", "-pdno", "name,serial"], capture_output=True, check=True) 93 | return dict(row.split() for row in p.stdout.decode().strip().split("\n")) 94 | except Exception: 95 | logging.exception("failed to read serials with lsblk, falling back to path tracking") 96 | return {} 97 | 98 | @wrap_exc 99 | def device_init(path: str, state: State): 100 | path = os.path.realpath(path, strict=True) 101 | try: 102 | with open(path, "rb") as f: 103 | size = f.seek(0, os.SEEK_END) 104 | except OSError as e: 105 | logging.error(f"device open error {path}: {e!r}") 106 | state.errors += 1 107 | return 108 | 109 | limit = size if not state.limit else min(size, state.limit) 110 | seed = path + "|" + os.urandom(8).hex() 111 | 112 | path_to_serial = read_path_to_serial() 113 | serial = path_to_serial.get(path) 114 | path_key = f"path:{path}" 115 | serial_key = f"serial:{serial}" if serial else None 116 | ideal_key = serial_key or path_key 117 | 118 | # upgrade path device to ideal device key 119 | if (device := state.devices.pop(path, None)) is not None: 120 | state.devices[ideal_key] = device 121 | 122 | elif (device := state.devices.pop(path_key, None)) is not None: 123 | state.devices[ideal_key] = device 124 | 125 | elif (device := state.devices.get(ideal_key)) is None: 126 | device = Device( 127 | path=path, 128 | size=size, 129 | limit=limit, 130 | read=0, 131 | wrote=0, 132 | errors=0, 133 | seed=seed, 134 | state="none", 135 | ) 136 | state.devices[ideal_key] = device 137 | 138 | assert device.size == size 139 | if serial is not None: 140 | device.serial = serial 141 | device.found = True 142 | 143 | @wrap_exc 144 | def device_work(device: Device, state: State) -> None: 145 | if not device.found: 146 | return 147 | 148 | try: 149 | blocksize = state.blocksize 150 | 151 | try: 152 | fd = os.open(device.path, os.O_WRONLY | os.O_DIRECT) 153 | except OSError as e: 154 | device.errors += 1 155 | logging.exception(f"device error: {device.path} {e!r}") 156 | return 157 | 158 | with os.fdopen(fd, "wb", buffering=-1) as f: 159 | size = f.seek(0, os.SEEK_END) 160 | device.state = "writing" 161 | 162 | rng = SeekSHUA(seed=device.seed, chunksize=state.seeksize, pos=device.wrote) 163 | buf = bytearray(blocksize) 164 | f.seek(device.wrote, os.SEEK_SET) 165 | for i in range(device.wrote, device.limit, blocksize): 166 | rng.fill(buf) 167 | n = f.write(buf) 168 | device.wrote += n 169 | if device.wrote < device.limit: 170 | logging.warning(f"{device.path} wrote short ({device.wrote} < {device.limit})") 171 | 172 | rng = SeekSHUA(seed=device.seed, chunksize=state.seeksize, pos=device.read) 173 | with open(device.path, "rb") as f: 174 | size = f.seek(0, os.SEEK_END) 175 | device.state = "reading" 176 | 177 | fbuf = bytearray(blocksize) 178 | rbuf = bytearray(blocksize) 179 | f.seek(device.read, os.SEEK_SET) 180 | for i in range(device.read, device.limit, blocksize): 181 | n = f.readinto(fbuf) 182 | rng.fill(rbuf) 183 | if fbuf[:n] != rbuf[:n]: 184 | device.errors += 1 185 | logging.error(f"{device.path} verify mismatch at {i}") 186 | device.read += n 187 | if device.read < device.limit: 188 | logging.warning(f"{device.path} read short ({device.read} < {device.limit})") 189 | device.state = "done" 190 | except Exception as e: 191 | raise Exception(f"device error: {device.path}") from e 192 | 193 | @wrap_exc 194 | def refresh_loop(state: State) -> None: 195 | def save(): 196 | if not state.save_path: 197 | return 198 | j = {"devices": {path: asdict(d) for path, d in state.devices.items()}} 199 | with open(state.save_path, "w") as f: 200 | json.dump(j, f) 201 | 202 | last_save = time.perf_counter() 203 | while True: 204 | now = time.perf_counter() 205 | if state.save_path and now - last_save > 60.0: 206 | save() 207 | last_save = now 208 | 209 | devices = state.devices.values() 210 | state.pbar.n = sum(dev.read + dev.wrote for dev in devices) 211 | state.pbar.total = sum(dev.limit for dev in devices) * 2 212 | drives_reading = sum(dev.state == "reading" for dev in devices) 213 | drives_writing = sum(dev.state == "writing" for dev in devices) 214 | drives_done = sum(dev.state == "done" for dev in devices) 215 | drives_missing = sum(not dev.found for dev in devices) 216 | missing_extra = f"(-{drives_missing}!)" if drives_missing else "" 217 | state.pbar.set_postfix({ 218 | "drives": f"{drives_writing}w+{drives_reading}r+{drives_done}/{len(state.devices)}" + missing_extra, 219 | "errors": sum(dev.errors for dev in devices) + state.errors, 220 | }) 221 | state.pbar.refresh() 222 | if drives_done == len(state.devices): 223 | save() 224 | break 225 | time.sleep(0.032) 226 | 227 | def parse_size(spec: str | None) -> int | None: 228 | if not spec: 229 | return None 230 | suffixes = "kmgtpe" 231 | spec = spec.lower() 232 | for i, s in enumerate(suffixes, start=1): 233 | if spec.endswith(s): 234 | scale = 1024 ** i 235 | spec = spec.removesuffix(s) 236 | size = int(spec) * scale 237 | break 238 | else: 239 | size = int(spec) 240 | assert size > 0 241 | return size 242 | 243 | def main(): 244 | parser = argparse.ArgumentParser() 245 | parser.add_argument("-j", "--jobs", type=int, default=None) 246 | parser.add_argument("-b", "--blocksize", type=int, default=1024 * 1024) 247 | parser.add_argument("--limit", type=str, default=None) 248 | parser.add_argument("--seeksize", type=str, default="25g") 249 | parser.add_argument("--resume", type=str, default=None) 250 | parser.add_argument("devices", nargs="+") 251 | args = parser.parse_args() 252 | 253 | state = State( 254 | pbar=None, 255 | blocksize=args.blocksize, 256 | limit=parse_size(args.limit), 257 | devices={}, 258 | save_path=Path(args.resume) if args.resume else None, 259 | seeksize=parse_size(args.seeksize), 260 | errors=0, 261 | ) 262 | 263 | if state.save_path: 264 | try: 265 | devset = set(args.devices) 266 | with open(state.save_path) as f: 267 | j = json.load(f) 268 | state.devices = {key: Device(**d) for key, d in j["devices"].items()} 269 | for device in state.devices.values(): 270 | device.found = False 271 | except FileNotFoundError: 272 | pass 273 | 274 | initial = sum(dev.read + dev.wrote for dev in state.devices.values()) 275 | with tqdm(unit_scale=True, unit="B", unit_divisor=1024, dynamic_ncols=True, initial=initial) as pbar: 276 | state.pbar = pbar 277 | 278 | with ThreadPoolExecutor(max_workers=args.jobs) as pool: 279 | for _ in pool.map(device_init, args.devices, [state] * len(args.devices)): 280 | pass 281 | pool.submit(refresh_loop, state) 282 | 283 | devices = list(state.devices.values()) 284 | finished_devs = [dev for dev in devices if dev.state == "done"] 285 | queued_devs = [dev for dev in devices if dev.state == "none"] 286 | active_devs = [dev for dev in devices if dev.state in ("reading", "writing")] 287 | random.shuffle(queued_devs) 288 | devices = finished_devs + queued_devs + active_devs 289 | 290 | for _ in pool.map(device_work, devices, [state] * len(devices)): 291 | pass 292 | 293 | if __name__ == "__main__": 294 | main() 295 | --------------------------------------------------------------------------------