forked from vivien/i3blocks-contrib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pre-merge safety checks and fixups Changes: - Fix invalid i3blocks.conf example - Pango escape all text that came from outside sources - Add INFO_TEXT_COLOR option, this also fixes a bug where very long statuslines appear without color - Remove duplicate unused getUnlockedCryptOutput function - Use list/tuple comprehensions for readability - Fix blank line regression
- Loading branch information
Showing
8 changed files
with
663 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# i3blocks-usb | ||
|
||
i3blocks-usb is an i3blocks blocket script to output connected usb storage device info. | ||
It supports usb mass storage devices, including those with multiple partitions, including LUKS partitions. | ||
|
||
 | ||
|
||
 | ||
|
||
 | ||
|
||
 | ||
|
||
|
||
# Requirements | ||
|
||
Dependencies: fonts-font-awesome, udev, python3, util-linux ( >= 2.23 ) | ||
|
||
Suggested: i3 ( >= 4.3 ), i3blocks ( >= 1.4 ) | ||
|
||
# Installation | ||
|
||
To use with i3blocks, put `usb.py` somewhere convenient. | ||
We will assume it is at `$SCRIPT_DIR/usb.py`. | ||
Copy the blocklet configuration in the given `i3blocks.conf` into your i3blocks configuration file. | ||
The recommended i3blocks config is | ||
|
||
```INI | ||
[usb] | ||
command=$SCRIPT_DIR/usb.py | ||
markup=pango | ||
signal=1 | ||
interval=10 | ||
``` | ||
|
||
To update the blocklet on plug/unplug device events you can add | ||
|
||
SUBSYSTEMS=="usb", RUN+="/usr/bin/pkill -RTMIN+1 i3blocks" | ||
|
||
in `/etc/udev/rules.d/70-persistent-usb.rules`. | ||
You may need to create the file. | ||
Then call | ||
|
||
```ShellSession | ||
sudo udevadm control --reload-rules | ||
``` | ||
|
||
to activate the new rules. | ||
If you do not care about updating the blocklet on plug/unplug, the script works fine on just an interval and you can ignore the udev rule and delete `signal=1` in the config. | ||
|
||
Now restart your i3 in place with | ||
|
||
```ShellSession | ||
i3-msg restart | ||
``` | ||
|
||
Try plugging in a usb device to make sure everything works. | ||
|
||
# Configuration | ||
|
||
Configuration is done by editing the top portion of `usb.py`. | ||
You will find several options that you can configure. | ||
Probably the most useful to you will be the ignore functions which allow you to ignore devices, e.g. those that are always plugged in. | ||
You do not need to restart i3 after making a change to the config. | ||
|
||
# Bugs | ||
|
||
Please report bugs and suggestions to the issues page. | ||
Contributions are always welcome. | ||
A common way a bug will manifest is that you will get no output in your bar. | ||
If this happens, try running `python3 $SCRIPT_DIR/usb.py` from the command line to get some insight into why nothing is displayed. | ||
You will probably see a python stack trace. | ||
Make sure to include this in your bug report, along with the output of any other commands that you may think are relevant (the stack trace may contain the exact system call that failed). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[usb] | ||
command=$SCRIPT_DIR/usb.py | ||
markup=pango | ||
signal=1 | ||
interval=10 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# Copyright (C) 2015 James Murphy | ||
# Licensed under the terms of the GNU GPL v2 only. | ||
# | ||
# i3blocks blocklet script to output connected usb storage device info. | ||
|
||
############################################################################### | ||
# BEGIN CONFIG | ||
# You may edit any of the following entries. DO NOT delete any of them, else | ||
# the main script will have unpredictable behavior. | ||
############################################################################### | ||
|
||
# Color options, can be a color name or #RRGGBB | ||
INFO_TEXT_COLOR = "white" | ||
MOUNTED_COLOR = "green" | ||
PLUGGED_COLOR = "gray" | ||
LOCKED_COLOR = "gray" | ||
UNLOCKED_NOT_MOUNTED_COLOR = "yellow" | ||
PARTITIONLESS_COLOR = "red" | ||
|
||
# Default texts | ||
PARTITIONLESS_TEXT = "no partitions" | ||
SEPARATOR = "<span color='gray'> | </span>" | ||
|
||
# FontAwesome unicode lock/unlock | ||
FA_LOCK = "\uf023" | ||
FA_UNLOCK = "\uf09c" | ||
|
||
# Maximum length of a filesystem label to display. Use None to disable | ||
# truncation, a positive integer to right truncate to that many characters, or | ||
# a negative integer to left truncate to that many characters. Setting this | ||
# option to 0 will disable the displaying of filesystem labels. | ||
TRUNCATE_FS_LABELS = None | ||
|
||
# Edit this function to ignore certain devices (e.g. those that are always | ||
# plugged in). | ||
# The dictionary udev_attributes_dict contains all the attributes given by | ||
# udevadm info --query=propery --name=$path | ||
def ignore(path, udev_attributes_dict): | ||
# E.g. how to ignore devices whose device name begins with /dev/sda | ||
#if udev_attributes_dict["DEVNAME"].startswith("/dev/sda"): | ||
# return True | ||
return False | ||
|
||
# Edit this function to ignore devices before the udev attributes are | ||
# computed in order to save time and memory. | ||
def fastIgnore(path): | ||
# E.g. how to to ignore devices whose path begins with /dev/sda | ||
#if path.startswith("/dev/sda"): | ||
# return True | ||
|
||
# E.g. how to ignore a fixed set of paths | ||
#if path in [ "/dev/path1", "/dev/path2", "/dev/path3" ]: | ||
# return True | ||
return False | ||
|
||
############################################################################### | ||
# END CONFIG | ||
# DO NOT EDIT ANYTHING AFTER THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING | ||
############################################################################### | ||
|
||
from subprocess import check_output | ||
|
||
def pangoEscape(text): | ||
return text.replace("&", "&").replace("<", "<").replace(">", ">") | ||
|
||
def getLeafDevicePaths(): | ||
lines = check_output(['lsblk', '-spndo', 'NAME'], universal_newlines=True) | ||
lines = lines.split("\n") | ||
lines = filter(None, lines) | ||
return lines | ||
|
||
def getKernelName(path): | ||
return check_output(['lsblk', '-ndso', 'KNAME', path], | ||
universal_newlines=True).rstrip("\n") | ||
|
||
def getDeviceType(path): | ||
return check_output(['lsblk', '-no', 'TYPE', path], | ||
universal_newlines=True).strip() | ||
|
||
def getFSType(path): | ||
global attributeMaps | ||
return attributeMaps[path].get("ID_FS_TYPE") | ||
|
||
def isLUKSPartition(path): | ||
return getFSType(path) == "crypto_LUKS" | ||
|
||
def isSwapPartition(path): | ||
return getFSType(path) == "swap" | ||
|
||
def getFSLabel(path): | ||
global attributeMaps | ||
label = attributeMaps[path].get("ID_FS_LABEL_ENC", "") | ||
if label: | ||
label = label.encode().decode("unicode-escape") | ||
if type(TRUNCATE_FS_LABELS) == int: | ||
if TRUNCATE_FS_LABELS >= 0: | ||
label = label[:TRUNCATE_FS_LABELS] | ||
elif TRUNCATE_FS_LABELS < 0: | ||
label = label[TRUNCATE_FS_LABELS:] | ||
return label | ||
|
||
def getFSOptions(path): | ||
lines = check_output(['findmnt', '-no', 'FS-OPTIONS', path], | ||
universal_newlines=True).strip() | ||
lines = lines.split(",") | ||
return lines | ||
|
||
def isReadOnly(path): | ||
return "ro" in getFSOptions(path) | ||
|
||
def isExtendedPartitionMarker(path): | ||
global attributeMaps | ||
MARKERS = ["0xf", "0x5"] | ||
return attributeMaps[path].get("ID_PART_ENTRY_TYPE") in MARKERS | ||
|
||
def getMountPoint(path): | ||
return check_output(['lsblk', '-ndo', 'MOUNTPOINT', path], | ||
universal_newlines=True).rstrip("\n") | ||
|
||
def getSpaceAvailable(path): | ||
lines = check_output(['df', '-h', '--output=avail', path], | ||
universal_newlines=True) | ||
lines = lines.split("\n") | ||
if len(lines) != 3: | ||
return "" | ||
else: | ||
return lines[1].strip() | ||
|
||
def getLockedCryptOutput(path): | ||
form = "<span color='{}'>[{} {}]</span>" | ||
kname = pangoEscape(getKernelName(path)) | ||
output = form.format(LOCKED_COLOR, FA_LOCK, kname) | ||
return output | ||
|
||
def getParentKernelName(path): | ||
lines = check_output(['lsblk', '-nso', 'KNAME', path], | ||
universal_newlines=True) | ||
lines = lines.split("\n") | ||
if len(lines) > 2: | ||
return lines[1].rstrip("\n") | ||
else: | ||
return "" | ||
|
||
def getUnlockedCryptOutput(path): | ||
mountPoint = getMountPoint(path) | ||
if mountPoint: | ||
color = MOUNTED_COLOR | ||
if isReadOnly(path): | ||
spaceAvail = "ro" | ||
else: | ||
spaceAvail = pangoEscape(getSpaceAvailable(path)) | ||
mountPoint = "<i>{}</i>:".format(pangoEscape(mountPoint)) | ||
else: | ||
color = UNLOCKED_NOT_MOUNTED_COLOR | ||
spaceAvail = "" | ||
kernelName = pangoEscape(getKernelName(path)) | ||
parentKernelName = pangoEscape(getParentKernelName(path)) | ||
|
||
block = "<span color='{}'>[{} {}:{}]</span>" | ||
block = block.format(color, FA_UNLOCK, parentKernelName, kernelName) | ||
|
||
label = pangoEscape(getFSLabel(path)) | ||
if label: | ||
label = '"{}"'.format(label) | ||
|
||
items = [block, label, mountPoint, spaceAvail] | ||
return " ".join(filter(None, items)) | ||
|
||
def getSwapOutput(path): | ||
return "" | ||
|
||
def getUnencryptedPartitionOutput(path): | ||
mountPoint = getMountPoint(path) | ||
if mountPoint: | ||
color = MOUNTED_COLOR | ||
if isReadOnly(path): | ||
spaceAvail = "ro" | ||
else: | ||
spaceAvail = pangoEscape(getSpaceAvailable(path)) | ||
mountPoint = "<i>{}</i>:".format(pangoEscape(mountPoint)) | ||
else: | ||
color = PLUGGED_COLOR | ||
spaceAvail = "" | ||
kernelName = pangoEscape(getKernelName(path)) | ||
|
||
block = "<span color='{}'>[{}]</span>" | ||
block = block.format(color, kernelName) | ||
|
||
label = pangoEscape(getFSLabel(path)) | ||
if label: | ||
label = '"{}"'.format(label) | ||
|
||
items = [block, label, mountPoint, spaceAvail] | ||
return " ".join(filter(None, items)) | ||
|
||
def getDiskWithNoPartitionsOutput(path): | ||
form = "<span color='{}'>[{}] {}</span>" | ||
kernelName = pangoEscape(getKernelName(path)) | ||
return form.format(PARTITIONLESS_COLOR, kernelName, PARTITIONLESS_TEXT) | ||
|
||
def getOutput(path): | ||
if isSwapPartition(path): | ||
return getSwapOutput(path) | ||
t = getDeviceType(path) | ||
if t == "part": | ||
if isExtendedPartitionMarker(path): | ||
return "" | ||
elif isLUKSPartition(path): | ||
return getLockedCryptOutput(path) | ||
else: | ||
return getUnencryptedPartitionOutput(path) | ||
elif t == "disk": | ||
return getDiskWithNoPartitionsOutput(path) | ||
elif t == "crypt": | ||
return getUnlockedCryptOutput(path) | ||
elif t == "rom" : | ||
return "" | ||
|
||
def makeAttributeMap(path): | ||
attributeMap = {} | ||
lines = check_output( | ||
['udevadm','info','--query=property','--name={}'.format(path)], | ||
universal_newlines=True) | ||
lines = lines.split("\n") | ||
for line in lines: | ||
if line: | ||
key, val = line.split("=", maxsplit=1) | ||
attributeMap[key] = val | ||
return attributeMap | ||
|
||
def getAttributeMaps(paths): | ||
return {path : makeAttributeMap(path) for path in paths} | ||
|
||
leaves = getLeafDevicePaths() | ||
leaves = [path for path in leaves if not fastIgnore(path)] | ||
attributeMaps = getAttributeMaps(leaves) | ||
leaves = (path for path in leaves if not ignore(path, attributeMaps[path])) | ||
outputs = filter(None, map(getOutput, leaves)) | ||
output = SEPARATOR.join(outputs) | ||
if output: | ||
output = "<span color='{}'>{}</span>".format(INFO_TEXT_COLOR, output) | ||
print(output) | ||
print(output) |