diff options
author | Ting-Wei Lan <lantw44@gmail.com> | 2015-01-29 21:19:32 +0800 |
---|---|---|
committer | Ting-Wei Lan <lantw44@gmail.com> | 2015-01-29 21:19:32 +0800 |
commit | ae8bb35d10c22e3c62b963b5b7f70b4475fd9b6f (patch) | |
tree | 70d5ec6f878dc63856a26e9788828409d283e320 | |
download | misc-tools-ae8bb35d10c22e3c62b963b5b7f70b4475fd9b6f.tar.gz misc-tools-ae8bb35d10c22e3c62b963b5b7f70b4475fd9b6f.tar.zst misc-tools-ae8bb35d10c22e3c62b963b5b7f70b4475fd9b6f.zip |
Initial commit - import things written for various hosts
-rwxr-xr-x | admin/rpmdu.sh | 50 | ||||
-rwxr-xr-x | admin/rpmsize.sh | 43 | ||||
-rwxr-xr-x | admin/search-login.py | 65 | ||||
-rwxr-xr-x | admin/ssh-bad-login.sh | 39 | ||||
-rwxr-xr-x | admin/yumdb-reason-editor.py | 198 | ||||
-rw-r--r-- | char/test-wchar.c | 31 | ||||
-rwxr-xr-x | desktop/empathy-xml2txt.py | 20 | ||||
-rwxr-xr-x | desktop/gen-ms-inet-shortcut.sh | 11 | ||||
-rwxr-xr-x | desktop/webkit1-gtk-user-agent.py | 11 | ||||
-rwxr-xr-x | desktop/webkit2-gtk-user-agent.py | 11 | ||||
-rwxr-xr-x | download/flickr-download-public.py | 51 | ||||
-rwxr-xr-x | image/set-time-from-exif-info.sh | 17 | ||||
-rw-r--r-- | network/in_addr-show.c | 23 | ||||
-rwxr-xr-x | network/unescape.js | 18 | ||||
-rwxr-xr-x | package/check-update-chromium.py | 36 | ||||
-rw-r--r-- | system/get-login.c | 7 | ||||
-rw-r--r-- | system/set-login.c | 15 | ||||
-rw-r--r-- | system/test-libacl.c | 469 | ||||
-rw-r--r-- | system/test-libcap.c | 206 |
19 files changed, 1321 insertions, 0 deletions
diff --git a/admin/rpmdu.sh b/admin/rpmdu.sh new file mode 100755 index 0000000..ef360b1 --- /dev/null +++ b/admin/rpmdu.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +div_base=1024 +div_name="KB" +if [ "`echo "$1" | cut -c 1`" = "-" ] +then + optname=`echo "$1" | cut -c 2-` + case "$optname" in + "k") + ;; + "m") + div_base=1048576 + div_name=MB + ;; + "g") + div_base=1073741824 + div_name=GB + ;; + *) + echo "Usage: `basename "$0"` [OPTION] package_name..." + echo " -k KB" + echo " -m MB" + echo " -g GB" + exit 1 + ;; + esac + shift +fi + +while [ "$1" ] +do + rpm -ql "$1" | { + while read oneline + do + if [ -f "$oneline" ] + then + du -k "$oneline" + fi + done + } | { + while read -d $'\t' filesize + do + total=$((${total}+${filesize})) + read + done + printf "%9d %s %s\n" "$(($total/$div_base))" "$div_name" "$1" + } + + shift +done + diff --git a/admin/rpmsize.sh b/admin/rpmsize.sh new file mode 100755 index 0000000..d884810 --- /dev/null +++ b/admin/rpmsize.sh @@ -0,0 +1,43 @@ +#!/bin/sh +div_base=1 +div_name="Bytes" +if [ "`echo "$1" | cut -c 1`" = "-" ] +then + optname=`echo "$1" | cut -c 2-` + case "$optname" in + "b") + ;; + "k") + div_base=1024 + div_name=KB + ;; + "m") + div_base=1048576 + div_name=MB + ;; + "g") + div_base=1073741824 + div_name=GB + ;; + *) + echo "Usage: `basename "$0"` [OPTION] package_name..." + echo " -b Byte" + echo " -k KB" + echo " -m MB" + echo " -g GB" + exit 1 + ;; + esac + shift +fi +while [ "$1" ] +do + total=0 + filesize=`rpm -q "$1" --queryformat "%{SIZE} "` + for i in $filesize + do + total=$((${total}+${i})) + done + printf "%12d %s %s\n" "$(($total/$div_base))" "$div_name" "$1" + shift +done diff --git a/admin/search-login.py b/admin/search-login.py new file mode 100755 index 0000000..ba87958 --- /dev/null +++ b/admin/search-login.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import sys +import re +from datetime import datetime + +if len(sys.argv) < 3: + sys.stderr.write( + 'Usage: {} output_of_last_FRw event_list\n'.format(sys.argv[0])) + quit(1) + +last_output = open(sys.argv[1], 'r') +event_list = open(sys.argv[2], 'r') +records = [] + +while True: + last_line_raw = last_output.readline().strip() + if not last_line_raw: + break + + last_line = last_line_raw + user_end_index = last_line.find(' ') + user = last_line[0:user_end_index] + if user == 'reboot': + continue + + last_line = last_line[user_end_index:len(last_line)].strip() + terminal_end_index = last_line.find(' ') + terminal = last_line[0:terminal_end_index] + + day_start_index = last_line.find(' ') + last_line = last_line[day_start_index:len(last_line)].strip() + date_start_index = last_line.find(' ') + last_line = last_line[date_start_index:len(last_line)].strip() + + login_time = last_line[0:20] + separator = last_line[20:23] + logout_time = last_line[27:47] + + login = datetime.strptime(login_time, '%b %d %H:%M:%S %Y') + if separator == ' - ' and not logout_time.startswith(' '): + logout = datetime.strptime(logout_time, '%b %d %H:%M:%S %Y') + else: + logout = datetime.max + + records.append({ + 'user': user, + 'tty': terminal, + 'login': login, + 'logout': logout, + 'raw': last_line_raw + }) + + +while True: + event_raw = event_list.readline().strip() + if not event_raw: + break + + event = event_raw + event_time = datetime.strptime(event, '%Y-%m-%d %H:%M:%S') + + for record in records: + if event_time >= record['login'] and event_time <= record['logout']: + print(event_time, '{0:16} => {1}'.format(record['user'], record['raw'])) diff --git a/admin/ssh-bad-login.sh b/admin/ssh-bad-login.sh new file mode 100755 index 0000000..69519d6 --- /dev/null +++ b/admin/ssh-bad-login.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +if [ "$1" ]; then + since="$1" +else + since="`date "+%Y-%m-%d"`" +fi + +if [ "$2" ]; then + until="$2" +else + until="now" +fi + +journalctl _SYSTEMD_UNIT=sshd.service \ + --since="$since" --until="$until" | \ + grep 'Failed password for' | \ + sed -e 's/.*from\ //g' -e 's/\ port.*//g' | sort -n | { + count=0; + firstrun=0; + printed=0; + while read oneline; + do + printed=0; + if [ "$prevline" != "$oneline" ] && [ "$firstrun" = "1" ]; + then + echo "$count $prevline"; + count=0; + printed=1; + fi + count=$(($count+1)); + prevline="$oneline"; + firstrun=1; + done + if [ "$printed" = "0" ]; + then + echo "$count $prevline"; + fi } | \ + sort -nr diff --git a/admin/yumdb-reason-editor.py b/admin/yumdb-reason-editor.py new file mode 100755 index 0000000..9a47691 --- /dev/null +++ b/admin/yumdb-reason-editor.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python2 + +import yum +from gi.repository import Gtk + + +class YumDB: + def __init__(self): + self.yb = yum.YumBase() + self.yb.conf + + def get_reasons(self, patterns=None): + reasons = [] + for pkg in sorted(self.yb.rpmdb.returnPackages(patterns=patterns)): + reasons.append({'nevra': pkg.nevra, \ + 'group_member': pkg.yumdb_info.group_member \ + if hasattr(pkg.yumdb_info, 'group_member') else '', \ + 'reason': pkg.yumdb_info.reason \ + if hasattr(pkg.yumdb_info, 'reason') else '<unset>', \ + 'reason_editor': pkg.yumdb_info.reason_editor \ + if hasattr(pkg.yumdb_info, 'reason_editor') else ''}) + return reasons + + def get_summary(self, nevra): + return self.yb.rpmdb.returnPackages(patterns=[ nevra ])[0].summary + + def get_description(self, nevra): + return self.yb.rpmdb.returnPackages(patterns=[ nevra ])[0].description + + def set_reason(self, nevra, value): + try: + pkg = self.yb.rpmdb.returnPackages(patterns=[ nevra ])[0] + except IndexError as e: + return '{} does not exists!'.format(nevra) + + try: + pkg.yumdb_info.reason = value + pkg.yumdb_info.reason_editor = 'modified' + except Exception as e: + return '{} fails: {}'.format(nevra, e[1]) + + return True + + +class PkgList(Gtk.TreeView): + def add_column(self, name, index): + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn(name) + column.pack_start(renderer, True) + column.add_attribute(renderer, 'text', index) + column.set_resizable(True) + column.set_sort_column_id(index) + column.set_sort_indicator(index) + self.append_column(column) + +class ReasonEditor(Gtk.Window): + COLUMN_NUMBER = 0 + COLUMN_NEVRA = 1 + COLUMN_GROUP = 2 + COLUMN_REASON = 3 + COLUMN_STATUS = 4 + + def __init__(self): + Gtk.Window.__init__(self, title='YumDB Reason Editor') + self.set_default_size(800, 600) + + self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) + + self.pkg_store = Gtk.ListStore(int, str, str, str, str) + self.pkg_list = PkgList(self.pkg_store) + self.pkg_list.add_column('#', 0) + self.pkg_list.add_column('Name-Version-Arch', 1) + self.pkg_list.add_column('Group', 2) + self.pkg_list.add_column('Reason', 3) + self.pkg_list.add_column('Status', 4) + self.pkg_list.set_enable_search(False) + self.pkg_list.set_search_column(self.COLUMN_NEVRA) + self.pkg_list.connect('row-activated', self.toggle_reason) + + self.pkg_scrollable = Gtk.ScrolledWindow() + self.pkg_scrollable.add(self.pkg_list) + + self.count_label = Gtk.Label() + + self.bottom_buttons = Gtk.Box() + self.reload_db = Gtk.Button.new_with_mnemonic(label='_Reload YumDB') + self.reload_db.connect('clicked', self.load_database) + self.show_info = Gtk.Button.new_with_mnemonic(label='_Info') + self.show_info.connect('clicked', self.show_summary_description) + self.enable_search = Gtk.ToggleButton('_Search Disabled') + self.enable_search.connect('toggled', self.toggle_search) + self.enable_search.set_use_underline(True) + self.bottom_buttons.pack_start(self.reload_db, True, True, 0) + self.bottom_buttons.pack_start(self.show_info, True, True, 0) + self.bottom_buttons.pack_start(self.enable_search, True, True, 0) + + self.vbox.pack_start(self.pkg_scrollable, True, True, 0) + self.vbox.pack_start(self.count_label, False, False, 0) + self.vbox.pack_start(self.bottom_buttons, False, False, 0) + self.add(self.vbox) + self.show_all() + + self.load_database() + + def load_database(self, unused=None): + self.yumdb = YumDB() + self.pkg_store.clear() + + print('Loading database ...') + data = self.yumdb.get_reasons() + + print('Adding entries ...') + self.dep_count = 0 + self.user_count = 0 + for index, entry in enumerate(data): + self.pkg_store.append([ index + 1, + entry['nevra'], entry['group_member'], + entry['reason'], entry['reason_editor'] ]) + if entry['reason'] == 'dep': + self.dep_count += 1 + else: + self.user_count += 1 + + self.pkg_count = index + 1 + self.update_count() + self.show_all() + + def update_count(self): + self.count_label.set_label('{} dep, {} user, {} pkgs'.format( + self.dep_count, self.user_count, self.pkg_count)) + + def toggle_search(self, unused=None): + if self.enable_search.get_active(): + self.enable_search.set_label('_Search Enabled') + self.pkg_list.set_enable_search(True) + else: + self.enable_search.set_label('_Search Disabled') + self.pkg_list.set_enable_search(False) + + def toggle_reason(self, path, column, unused=None): + model = self.pkg_list.get_model() + tree_path = self.pkg_list.get_cursor()[0] + if tree_path == None: + return + + tree_iter = model.get_iter(tree_path) + if tree_iter == None: + return + + nevra = model[tree_iter][self.COLUMN_NEVRA] + is_dep = model[tree_iter][self.COLUMN_REASON] == 'dep' + + result = self.yumdb.set_reason(nevra, 'user' if is_dep else 'dep') + if result != True: + error_dialog = Gtk.MessageDialog( + title='YumDB Error', + buttons=Gtk.ButtonsType.CLOSE, + message_type=Gtk.MessageType.ERROR, + message_format=result) + error_dialog.run() + error_dialog.destroy() + return + + new_entry = self.yumdb.get_reasons([ nevra ])[0] + model[tree_iter][self.COLUMN_REASON] = new_entry['reason'] + model[tree_iter][self.COLUMN_STATUS] = new_entry['reason_editor'] + tree_path.next() + self.pkg_list.set_cursor(tree_path) + if is_dep: + self.dep_count -= 1 + self.user_count += 1 + else: + self.dep_count += 1 + self.user_count -= 1 + self.update_count() + + def show_summary_description(self, unused=None): + model, tree_iter = self.pkg_list.get_selection().get_selected() + if tree_iter == None: + return + nevra = model[tree_iter][1] + + dialog = Gtk.MessageDialog( + title=nevra, + buttons=Gtk.ButtonsType.CLOSE, + message_type=Gtk.MessageType.INFO, + message_format=None) + dialog.set_markup('<big><b>{}</b></big>\n\n{}'.format( + self.yumdb.get_summary(nevra), + self.yumdb.get_description(nevra))) + dialog.run() + dialog.destroy() + + +if __name__ == '__main__': + editor = ReasonEditor() + editor.connect('delete-event', Gtk.main_quit) + Gtk.main() diff --git a/char/test-wchar.c b/char/test-wchar.c new file mode 100644 index 0000000..ab37604 --- /dev/null +++ b/char/test-wchar.c @@ -0,0 +1,31 @@ +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <wchar.h> + +int main () { + setlocale (LC_ALL, ""); + + char *line = NULL; + ssize_t str_len = getline (&line, &(size_t){ 0 }, stdin); + + mbstate_t mbs; + wchar_t wc; + size_t mbrlen_result; + int wcwidth_result; + + memset (&mbs, 0, sizeof (mbs)); + mbrlen_result = mbrlen (line, str_len, &mbs); + + memset (&mbs, 0, sizeof (mbs)); + mbrtowc (&wc, line, str_len, &mbs); + wcwidth_result = wcwidth (wc); + + printf ("mbrlen = %zu, wcwidth = %d\n", mbrlen_result, wcwidth_result); + free (line); + return 0; +} diff --git a/desktop/empathy-xml2txt.py b/desktop/empathy-xml2txt.py new file mode 100755 index 0000000..9c6a7a0 --- /dev/null +++ b/desktop/empathy-xml2txt.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import sys +from lxml import etree + +def empathy_xml2txt(infile, outfile): + xmlparser = etree.XMLParser(encoding = 'UTF-8') + xmltree = etree.parse(infile, xmlparser) + xmlroot = xmltree.getroot() + for msg in xmlroot.iter(): + if msg.tag == "message": + outfile.write("{} <{:^12}> {}\n".format( + msg.get('time').replace('T', ' '), + msg.get('id'), + msg.text.encode('UTF-8'))) + +if __name__ == "__main__": + for i in sys.argv[1:]: + empathy_xml2txt(i, sys.stdout) diff --git a/desktop/gen-ms-inet-shortcut.sh b/desktop/gen-ms-inet-shortcut.sh new file mode 100755 index 0000000..0e6aeea --- /dev/null +++ b/desktop/gen-ms-inet-shortcut.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +[ "$#" != "2" ] && { + echo "$0: Need 2 arguments" + echo "Usage: $0 filename url" +} && exit 1 + +{ + echo "[InternetShortcut]" + echo "URL=$2" +} > "$1" diff --git a/desktop/webkit1-gtk-user-agent.py b/desktop/webkit1-gtk-user-agent.py new file mode 100755 index 0000000..9ea72ba --- /dev/null +++ b/desktop/webkit1-gtk-user-agent.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from gi.repository import WebKit + +version = '{}.{}.{}'.format( + WebKit.major_version(), + WebKit.minor_version(), + WebKit.micro_version()) +user_agent = WebKit.WebView().get_settings().get_user_agent() + +print('version = {}, user-agent = {}'.format(version, user_agent)) diff --git a/desktop/webkit2-gtk-user-agent.py b/desktop/webkit2-gtk-user-agent.py new file mode 100755 index 0000000..1af74e4 --- /dev/null +++ b/desktop/webkit2-gtk-user-agent.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from gi.repository import WebKit2 + +version = '{}.{}.{}'.format( + WebKit2.get_major_version(), + WebKit2.get_minor_version(), + WebKit2.get_micro_version()) +user_agent = WebKit2.WebView().get_settings().get_user_agent() + +print('version = {}, user-agent = {}'.format(version, user_agent)) diff --git a/download/flickr-download-public.py b/download/flickr-download-public.py new file mode 100755 index 0000000..91fbeef --- /dev/null +++ b/download/flickr-download-public.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +flickr_api_key = 'e65a9f9957d8f6ae7490072452c0a345' +flickr_group_id = '<Insert your flickr group ID here>' + +def flickr_url(page): + return '{}?api_key={}&method={}&group_id={}&extras={}&page={}&per_page={}'.format( + 'https://api.flickr.com/services/rest/', + flickr_api_key, 'flickr.photos.search', + flickr_group_id, 'url_o', page, 500) + + +import os +import urllib.request +from xml.etree import ElementTree + +def flickr_request(page): + url = flickr_url(page) + print('Retrieving information from page {} ...'.format(page), end='', flush=True) + info = urllib.request.urlopen(url) + print(' done') + return info.readall() + +def flickr_download(url): + fname = os.path.basename(urllib.parse.urlsplit(url).path) + try: + f = open(fname, 'xb') + except FileExistsError: + print('Skipping {}'.format(url)) + return + + print('Downloading {} ...'.format(url), end='', flush=True) + data = urllib.request.urlopen(url).readall() + print(' done') + f.write(data) + f.close() + +info = ElementTree.fromstring(flickr_request(1)) +pages = int(info.find('photos').get('pages')) +photos = int(info.find('photos').get('total')) +print('There are {} photos ({} pages)'.format(photos, pages)) + +count = 0 +for page in range(1, pages + 1): + info = ElementTree.fromstring(flickr_request(page)) + for node in info.iter(): + if node.tag == 'photo': + count = count + 1 + print('[{}/{}] '.format(count, photos), end='', flush=True) + url = node.get('url_o') + flickr_download(url) diff --git a/image/set-time-from-exif-info.sh b/image/set-time-from-exif-info.sh new file mode 100755 index 0000000..bd50997 --- /dev/null +++ b/image/set-time-from-exif-info.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +filter_exiftool_output () { + exiftool -d '%Y%m%d%H%M.%S' "$1" | grep "$2" | tail -n 1 | awk '{print $4}' +} + +count=0 +for i in *; do + count=$(( $count + 1 )) + printf "%4d: Touching %s ..." "$count" "$i" + datetime="`filter_exiftool_output "$i" '^Date/Time'`" + : ${datetime:="`filter_exiftool_output "$i" '^Create Date'`"} + : ${datetime:="`filter_exiftool_output "$i" '^Create'`"} + : ${datetime:="`filter_exiftool_output "$i" '^Modify'`"} + printf " %s\n" "$datetime" + touch -t "$datetime" "$i" +done diff --git a/network/in_addr-show.c b/network/in_addr-show.c new file mode 100644 index 0000000..e8dd01b --- /dev/null +++ b/network/in_addr-show.c @@ -0,0 +1,23 @@ +#include <arpa/inet.h> +#include <stdint.h> +#include <stdio.h> +#include <netinet/in.h> +#include <sys/socket.h> + +int main (int argc, char *argv[]) { + char ipstr[INET_ADDRSTRLEN]; + unsigned long ipbin; + + for (int i = 1; i < argc; i++) { + if (sscanf (argv[i], "%lx", &ipbin) <= 0) { + return 1; + } + struct in_addr ipaddr = { .s_addr = ipbin }; + if (inet_ntop (AF_INET, &ipaddr, ipstr, INET_ADDRSTRLEN) == NULL) { + return 2; + } + puts (ipstr); + } + + return 0; +} diff --git a/network/unescape.js b/network/unescape.js new file mode 100755 index 0000000..fadca7b --- /dev/null +++ b/network/unescape.js @@ -0,0 +1,18 @@ +#!/usr/bin/env seed + +const Gio = imports.gi.Gio; + +var path = '/dev/stdin'; +if (Seed.argv.length >= 3) { + path = Seed.argv[2]; +} + +var file = Gio.file_new_for_path(path); +var istream = new Gio.DataInputStream.c_new(file.read()); + +var line; +while ((line = istream.read_line_utf8()) != undefined) { + print(unescape(line.replace(/\+/g, ' '))); +} + +istream.close(); diff --git a/package/check-update-chromium.py b/package/check-update-chromium.py new file mode 100755 index 0000000..74942c5 --- /dev/null +++ b/package/check-update-chromium.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +from pkg_resources import parse_version +import json +import urllib.request + + +def get_upstream_version(): + url = 'http://omahaproxy.appspot.com/all?os=linux&channel=stable' + data = urllib.request.urlopen(url).readall() + return data.split(b'\n')[1].split(b',')[2].decode() + +def get_package_version_cgit(): + cgit_plain_url = 'http://phantom.tfcis.org/~lantw44/cgit/copr-rpm-spec/plain/chromium/chromium/chromium.spec' + spec = urllib.request.urlopen(cgit_plain_url).readall().decode() + return spec.split('\nVersion:')[1].split()[0] + +def get_package_version_copr(): + copr_api_url = 'https://copr.fedoraproject.org/api' + url = copr_api_url + '/coprs/lantw44/chromium/monitor/' + data = json.loads(urllib.request.urlopen(url).readall().decode()) + for pkg in data['packages']: + if pkg['pkg_name'] == 'chromium': + return pkg['pkg_version'] + +get_package_version = get_package_version_copr +latest = get_upstream_version() +current = get_package_version() + +import sys +if len(sys.argv) >= 2: + print('>>> latest = {}, packaged = {}'.format(latest, current)) + +if parse_version(current) < parse_version(latest): + print('Please update the chromium package!') + print('>>> latest = {}, packaged = {}'.format(latest, current)) diff --git a/system/get-login.c b/system/get-login.c new file mode 100644 index 0000000..a73514a --- /dev/null +++ b/system/get-login.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <unistd.h> + +int main () { + printf ("getlogin() = %s\n", getlogin()); + return 0; +} diff --git a/system/set-login.c b/system/set-login.c new file mode 100644 index 0000000..d00ad51 --- /dev/null +++ b/system/set-login.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <unistd.h> + +int main (int argc, char* argv[]) { + if (argc < 2){ + fprintf (stderr, "One Argument required.\n"); + return 1; + } + printf ("setlogin(%s)\n", argv[1]); + if (setlogin (argv[1]) < 0){ + perror("setlogin"); + return 2; + } + return 0; +} diff --git a/system/test-libacl.c b/system/test-libacl.c new file mode 100644 index 0000000..27ccc5a --- /dev/null +++ b/system/test-libacl.c @@ -0,0 +1,469 @@ +#ifdef __FreeBSD__ +# define _WITH_GETLINE +#else +# define _POSIX_C_SOURCE 200809L +# define _XOPEN_SOURCE 700 +#endif + +#include <assert.h> +#include <errno.h> +#include <grp.h> +#include <locale.h> +#include <pwd.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/acl.h> +#include <unistd.h> + +// Needed to access non-portable functions on Linux +#ifdef __SYS_ACL_H +# include <acl/libacl.h> +#endif + +typedef enum { + CMD_MODIFY_ACTION_EDIT, + CMD_MODIFY_ACTION_DELETE, + CMD_MODIFY_ACTION_LAST, +} CmdModifyAction; + + +static void print_function_result (bool success, const char* function){ + if (success) { + printf ("\033[1;32m%s\033[0m: OK\n", function); + } else { + printf ("\033[1;31m%s\033[0m: %s\n", function, strerror (errno)); + } +} + +static bool check_error_int (int rval, const char* function) { + if (rval < 0){ + print_function_result (false, function); + return true; + } else { + print_function_result (true, function); + return false; + } +} + +static bool check_error_ptr (void* rval, const char* function) { + if (rval == NULL) { + print_function_result (false, function); + return true; + } else { + print_function_result (true, function); + return false; + } +} + +static const char* read_input (const char* prompt) { + static char* buffer_ptr = NULL; + static size_t buffer_len = 0; + + clearerr (stdin); + fputs (prompt, stdout); + fflush (stdout); + + ssize_t str_len = getline (&buffer_ptr, &buffer_len, stdin); + if (str_len < 0) { + free (buffer_ptr); + buffer_ptr = NULL; + buffer_len = 0; + return ""; + } + + buffer_ptr[str_len - 1] = '\0'; + return buffer_ptr; +} + +static acl_type_t read_acl_type (void) { +#define READ_ACL_TYPE_INVALID (-1) + static_assert (ACL_TYPE_ACCESS != READ_ACL_TYPE_INVALID, "ACL_TYPE_ACCESS == READ_ACL_TYPE_INVALID"); + static_assert (ACL_TYPE_DEFAULT != READ_ACL_TYPE_INVALID, "ACL_TYPE_DEFAULT == READ_ACL_TYPE_INVALID"); +#ifdef ACL_TYPE_NFS4 + static_assert (ACL_TYPE_NFS4 != READ_ACL_TYPE_INVALID, "ACL_TYPE_NFS4 == READ_ACL_TYPE_INVALID"); + const char* input = read_input ("Mode [1=access 2=default 3=nfsv4]: "); +#else + const char* input = read_input ("Mode [1=access 2=default]: "); +#endif + + switch (input[0]) { + case '1': + return ACL_TYPE_ACCESS; + case '2': + return ACL_TYPE_DEFAULT; +#ifdef ACL_TYPE_NFS4 + case '3': + return ACL_TYPE_NFS4; +#endif + default: + return READ_ACL_TYPE_INVALID; + } + + return READ_ACL_TYPE_INVALID; +} + +static void entry_display (acl_entry_t entry, unsigned int count) { + acl_tag_t tag; + acl_permset_t permset; + + void* qualifier_ptr; + uid_t* uid_ptr; + gid_t* gid_ptr; + + if (check_error_int (acl_get_tag_type (entry, &tag), "acl_get_tag_type")) { + return; + } + if (tag == ACL_USER || tag == ACL_GROUP) { + if (check_error_ptr (qualifier_ptr = acl_get_qualifier (entry), "acl_get_qualifier")) { + return; + } + } + if (check_error_int (acl_get_permset (entry, &permset), "acl_get_permset")) { + return; + } + + printf ("%u: ", count); + switch (tag) { + case ACL_USER: + case ACL_USER_OBJ: + putchar ('u'); + break; + case ACL_GROUP: + case ACL_GROUP_OBJ: + putchar ('g'); + break; + case ACL_OTHER: + putchar ('o'); + break; + case ACL_MASK: + putchar ('m'); + } + putchar (':'); + + if (tag == ACL_USER) { + struct passwd* pwd; + uid_ptr = qualifier_ptr; + errno = 0; + if ((pwd = getpwuid (*uid_ptr)) == NULL) { + printf ("\033[1;31m%lu\033[0m", (unsigned long)*uid_ptr); + } else { + printf ("%s(%lu)", pwd->pw_name, (unsigned long)*uid_ptr); + } + } else if (tag == ACL_GROUP) { + struct group* grp; + gid_ptr = qualifier_ptr; + errno = 0; + if ((grp = getgrgid (*gid_ptr)) == NULL) { + printf ("\033[1;31m%lu\033[0m", (unsigned long)*gid_ptr); + } else { + printf ("%s(%lu)", grp->gr_name, (unsigned long)*gid_ptr); + } + } + putchar (':'); + +#ifdef _SYS_ACL_H_ // TrustedBSD and FreeBSD + putchar (acl_get_perm_np (permset, ACL_READ) ? 'r' : '-'); + putchar (acl_get_perm_np (permset, ACL_WRITE) ? 'w' : '-'); + putchar (acl_get_perm_np (permset, ACL_EXECUTE) ? 'x' : '-'); +#elif defined(__SYS_ACL_H) // Linux and possibly Hurd or other GNU systems + putchar (acl_get_perm (permset, ACL_READ) ? 'r' : '-'); + putchar (acl_get_perm (permset, ACL_WRITE) ? 'w' : '-'); + putchar (acl_get_perm (permset, ACL_EXECUTE) ? 'x' : '-'); +#else +# error "Sorry, your operating system is not supported." +#endif +} + +static void entry_edit(acl_entry_t* entry){ + acl_tag_t tag; + const char* input; + + input = read_input ("Tag [u g uo go o m]: "); + switch (input[0]) { + case 'u': + switch (input[1]) { + case '\0': + tag = ACL_USER; + break; + case 'o': + tag = ACL_USER_OBJ; + break; + default: + return; + } + break; + case 'g': + switch (input[1]){ + case '\0': + tag = ACL_GROUP; + break; + case 'o': + tag = ACL_GROUP_OBJ; + break; + default: + return; + } + break; + case 'o': + tag = ACL_OTHER; + break; + case 'm': + tag = ACL_MASK; + break; + default: + return; + } + + if (check_error_int (acl_set_tag_type (*entry, tag), "acl_set_tag_type")) { + return; + } + + if (tag == ACL_USER) { + input = read_input ("User [user name or #uid]: "); + uid_t uid; + if (input[0] == '#') { + uid = (uid_t)atol (&input[1]); + } else { + struct passwd* pwd; + errno = 0; + if (check_error_ptr (pwd = getpwnam (input), "getpwnam")) { + return; + } + uid = pwd->pw_uid; + } + if (check_error_int (acl_set_qualifier (*entry, &uid), "acl_set_qualifier")) { + return; + } + } else if (tag == ACL_GROUP) { + input = read_input ("Group [group name or #gid]: "); + gid_t gid; + if (input[0] == '#') { + gid = (gid_t)atol (&input[1]); + } else { + struct group* grp; + errno = 0; + if (check_error_ptr (grp = getgrnam (input), "getgrnam")) { + return; + } + gid = grp->gr_gid; + } + if (check_error_int (acl_set_qualifier (*entry, &gid), "acl_set_qualifier")) { + return; + } + } + + input = read_input ("Permission: "); + size_t len = strlen (input); + + acl_permset_t permset; + if (check_error_int (acl_get_permset (*entry, &permset), "acl_get_permset")) { + return; + } + if (check_error_int (acl_clear_perms (permset), "acl_clear_perms")) { + return; + } + for (size_t i = 0; i < len; i++) { + switch (input[i]) { + case 'r': + if (check_error_int (acl_add_perm (permset, ACL_READ), "acl_add_perm")) { + return; + } + break; + case 'w': + if (check_error_int (acl_add_perm (permset, ACL_WRITE), "acl_add_perm")) { + return; + } + break; + case 'x': + if (check_error_int (acl_add_perm (permset, ACL_EXECUTE), "acl_add_perm")) { + return; + } + } + } +} + +static void cmd_add (acl_t* alist) { + acl_entry_t entry; + + if (check_error_int (acl_create_entry (alist, &entry), "acl_create_entry")) { + return; + } + entry_edit (&entry); +} + +static void cmd_modify (const acl_t* alist, CmdModifyAction action) { + acl_entry_t entry; + int rval = acl_get_entry (*alist, ACL_FIRST_ENTRY, &entry); + unsigned int count = 0; + + do { + count++; + switch (rval) { + case 1: + check_error_int (rval, "acl_get_entry"); + break; + case 0: + return; + case -1: + check_error_int (rval, "acl_get_entry"); + return; + } + + entry_display (entry, count); + putchar(' '); + + switch (action) { + case CMD_MODIFY_ACTION_DELETE: { + const char* input = read_input ("Delete [y/n/q]? "); + switch (input[0]) { + case 'q': + return; + case 'y': + check_error_int (acl_delete_entry (*alist, entry), "acl_delete_entry"); + } + } break; + + case CMD_MODIFY_ACTION_EDIT: { + const char* input = read_input ("Edit [y/n/q]? "); + switch (input[0]) { + case 'q': + return; + case 'y': + entry_edit (&entry); + } + } break; + + default: + puts ("This should never happen"); + return; + } + } while ((rval = acl_get_entry (*alist, ACL_NEXT_ENTRY, &entry)) == 1); +} + +static void cmd_read (acl_t* alist){ + acl_t alist_new; + + char* name = strdup (read_input ("File name= ")); + acl_type_t type = read_acl_type (); + + if (type == READ_ACL_TYPE_INVALID) { + free (name); + return; + } + + if (!check_error_ptr (alist_new = acl_get_file (name, type), "acl_get_file")) { + check_error_int (acl_free (*alist), "acl_free"); + *alist = alist_new; + } + free (name); +} + +static void cmd_write(const acl_t* alist){ + char* name = strdup (read_input ("File name= ")); + acl_type_t type = read_acl_type (); + + if (type == READ_ACL_TYPE_INVALID) { + free (name); + return; + } + + check_error_int (acl_set_file (name, type, *alist), "acl_set_file"); + free (name); +} + +static void cmd_zzz (void){ + const char* name = read_input ("File name= "); + check_error_int (acl_delete_def_file (name), "acl_delete_def_file"); +} + +int main (int argc, char* argv[]) { + setlocale (LC_ALL, ""); + + acl_t alist; + + if (check_error_ptr (alist = acl_init (4), "acl_init")) { + return 1; + } + + while (true) { + const char* input = read_input ("\033[1;33macl>\033[0m "); + + if (feof (stdin)) { + goto loop_end; + } + + switch (input[0]) { + case 'a': + cmd_add (&alist); + break; + case 'c': + check_error_int (acl_calc_mask (&alist), "acl_calc_mask"); + break; + case 'd': + cmd_modify (&alist, CMD_MODIFY_ACTION_DELETE); + break; + case 'e': + cmd_modify (&alist, CMD_MODIFY_ACTION_EDIT); + break; + case 'i': { + acl_t alist_new; + int count; + input = read_input ("Count= "); + count = atoi (input); + if (!check_error_ptr (alist_new = acl_init (count), "acl_init")) { + check_error_int (acl_free (alist), "acl_free"); + alist = alist_new; + } + } break; + case 'p': { + char* atext; + if (!check_error_ptr (atext = acl_to_text (alist, NULL), "acl_to_text")) { + fputs (atext, stdout); + if (atext[strlen (atext) - 1] != '\n') { + putchar ('\n'); + } + check_error_int (acl_free (atext), "acl_free"); + } + } break; + case 'q': + goto loop_end; + case 'r': + cmd_read (&alist); + break; + case 'v': + if (acl_valid (alist) == 0) { + puts ("Current ACL structure is \033[1;32mvalid\033[0m."); + } else { + puts ("Current ACL structure is \033[1;31minvalid\033[0m."); + } + break; + case 'w': + cmd_write (&alist); + break; + case 'z': + cmd_zzz (); + break; + default: + fputs ( + " a Add an ACL entry\n" + " c Automatically generate the mask value\n" + " d Delete ACL entries\n" + " e Edit ACL entries\n" + " i Clear and initialize an empty ACL structure\n" + " p Print current ACL structure\n" + " q Quit this program\n" + " r Read ACL structure from file\n" + " v Verify current ACL\n" + " w Write ACL structure to file\n" + " z Delete default ACL entries from file\n" + , stdout); + } + } + +loop_end: + check_error_int (acl_free (alist), "acl_free"); + return 0; +} diff --git a/system/test-libcap.c b/system/test-libcap.c new file mode 100644 index 0000000..806795e --- /dev/null +++ b/system/test-libcap.c @@ -0,0 +1,206 @@ +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 + +#include <ctype.h> +#include <errno.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capability.h> + +static void print_function_result (bool success, const char* function){ + if (success) { + printf ("\033[1;32m%s\033[0m: OK\n", function); + } else { + printf ("\033[1;31m%s\033[0m: %s\n", function, strerror (errno)); + } +} + +static bool check_error_int (int rval, const char* function) { + if (rval < 0){ + print_function_result (false, function); + return true; + } else { + print_function_result (true, function); + return false; + } +} + +static bool check_error_ptr (void* rval, const char* function) { + if (rval == NULL) { + print_function_result (false, function); + return true; + } else { + print_function_result (true, function); + return false; + } +} + +static const char* read_input (const char* prompt) { + static char* buffer_ptr = NULL; + static size_t buffer_len = 0; + + clearerr (stdin); + fputs (prompt, stdout); + fflush (stdout); + + ssize_t str_len = getline (&buffer_ptr, &buffer_len, stdin); + if (str_len < 0) { + free (buffer_ptr); + buffer_ptr = NULL; + buffer_len = 0; + return ""; + } + + buffer_ptr[str_len - 1] = '\0'; + return buffer_ptr; +} + +int main (int argc, char* argv[]) { + setlocale (LC_ALL, ""); + + cap_t store = NULL; + + while (true) { + const char* input = read_input ("\033[1;33mcap>\033[m "); + + if (feof (stdin)) { + goto loop_end; + } + + switch (input[0]){ + case 'c': { + cap_flag_t flag; + input = read_input ("[E]ffective [P]ermitted [I]nheritable ? "); + switch (toupper (input[0])) { + case 'E': + flag = CAP_EFFECTIVE; + break; + case 'P': + flag = CAP_PERMITTED; + break; + case 'I': + flag = CAP_INHERITABLE; + break; + default: + continue; + } + check_error_int (cap_clear_flag (store, flag), "cap_clear_flag"); + } break; + case 'e': { + cap_flag_t flag; + input = read_input ("[E]ffective [P]ermitted [I]nheritable ? "); + switch (toupper (input[0])) { + case 'E': + flag = CAP_EFFECTIVE; + break; + case 'P': + flag = CAP_PERMITTED; + break; + case 'I': + flag = CAP_INHERITABLE; + break; + default: + continue; + } + + cap_value_t value; + input = read_input ("Capability= "); + if (check_error_int (cap_from_name (input, &value), "cap_from_name")) { + break; + } + + cap_flag_value_t flag_value; + input = read_input ("[C]lear [S]et ? "); + switch (toupper (input[0])) { + case 'C': + case 'R': + case '0': + flag_value = CAP_CLEAR; + break; + case 'S': + case '1': + flag_value = CAP_SET; + break; + } + + check_error_int ( + cap_set_flag (store, flag, 1, &value, flag_value), + "cap_set_flag"); + } break; + case 'i': + if (store != NULL) { + check_error_int (cap_free (store), "cap_free"); + store = NULL; + } + check_error_ptr (store = cap_init (), "cap_init"); + break; + case 'f': + if (store == NULL) { + break; + } + check_error_int (cap_free (store), "cap_free"); + store = NULL; + break; + case 'r': + if (store != NULL){ + check_error_int (cap_free(store), "cap_free"); + store = NULL; + } + input = read_input ("[P]rocess [F]ile ? "); + switch(toupper (input[0])){ + case 'P': + case '1': + input = read_input ("PID= "); + check_error_ptr ( + store = cap_get_pid ((pid_t)atol (input)), + "cap_get_pid"); + break; + case 'F': + case '2': + input = read_input ("File= "); + check_error_ptr ( + store = cap_get_file (input), + "cap_set_file"); + break; + } + break; + case 'w': + if (store != NULL) { + input = read_input ("File= "); + check_error_int (cap_set_file (input, store), "cap_set_file"); + } + break; + case 'p': { + char *ctext; + if (store == NULL) { + break; + } + check_error_ptr (ctext = cap_to_text (store, NULL), "cap_to_text"); + if (ctext != NULL) { + puts (ctext); + check_error_int (cap_free (ctext), "cap_free"); + } + } break; + default: + fputs ( + " c\tClear a capability set\n" + " e\tEdit the capability data structure\n" + " f\tFree the capability data structure\n" + " i\tClear and Initialize a new capability\n" + " p\tPrint the current capability\n" + " r\tRead and Replace the capability\n" + " w\tSet the current capability to a process or a file\n", + stdout); + } + } + +loop_end: + if (store != NULL) { + check_error_int (cap_free (store), "cap_free"); + store = NULL; + } + return 0; +} |