Skip to content

Commit 2a5f298

Browse files
Migrate max files to macos section and add feature flag for it
1 parent 1b528cc commit 2a5f298

8 files changed

Lines changed: 360 additions & 25 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ Key scripts designed to boost developer productivity:
8787
- **Usage**: `rn-fix`
8888
- **Saves Time**: 5-10 minutes of manual cleanup per fix
8989

90+
### macOS File Descriptor Management
91+
92+
**`fix-max-files-temp`** - Temporary File Descriptor Limit
93+
94+
- **Problem Solved**: "Too many open files" errors when running file watchers or tools that open many file descriptors
95+
- **How It Works**: Applies temporary system-level file descriptor limits for the current session using `launchctl`
96+
- **Usage**: `fix-max-files-temp`
97+
- **Duration**: Session only; resets on reboot
98+
- **Perfect For**: Quick fixes during development sessions
99+
100+
**`fix-max-files-permanently`** - Permanent File Descriptor Configuration
101+
102+
- **Problem Solved**: Sets persistent file descriptor limits across reboots
103+
- **How It Works**: Installs LaunchDaemon configuration at `/Library/LaunchDaemons/limit.maxfiles.plist`
104+
- **Usage**: `fix-max-files-permanently`
105+
- **Requirements**: The `limit.maxfiles.plist` file should be located alongside the script
106+
- **Warning**: Will not override an existing configuration file
107+
- **Perfect For**: Permanent solution on development machines
108+
90109
**`rnios` / `rnra`** - iOS/Android Launch Shortcuts
91110

92111
- **Usage**: `rnios` (iOS) or `rnra` (Android)

apps/watchman/set-max-file-limit.sh

Lines changed: 0 additions & 24 deletions
This file was deleted.

entry.zsh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ if [[ "${OSA_CONFIG_COMPONENTS_MAC_TOOLS}" == "true" ]] || [[ "${OSA_CONFIG_SNIP
132132
[[ -f "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/rmAsync.zsh" ]] && source "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/rmAsync.zsh"
133133
fi
134134

135+
# macOS File Descriptor Management
136+
if [[ "${OSA_CONFIG_SNIPPETS_OSASNIPPETS_FILELIMITS}" == "true" ]] || [[ "${OSA_CONFIG_SNIPPETS_OSASNIPPETS_FILELIMITS}" == "true" ]]; then
137+
[[ -f "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/set-max-file-limit.sh" ]] && source "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/set-max-file-limit.sh"
138+
fi
139+
135140
# macOS eGPU Management
136141
if [[ "${OSA_CONFIG_COMPONENTS_EGPU}" == "true" ]] || [[ "${OSA_CONFIG_SNIPPETS_OSASNIPPETS_EGPU}" == "true" ]]; then
137142
[[ -f "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/egpu.zsh" ]] && source "$OSA_SCRIPTS_ZSH_ROOT/platform/mac/egpu.zsh"
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env zsh
2+
set -euo pipefail
3+
4+
command -v adb >/dev/null 2>&1 || { echo "adb not found in PATH" >&2; exit 1; }
5+
6+
normalize() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
7+
8+
guess_type() {
9+
local label="$1"
10+
local flags="$2"
11+
local id="$3"
12+
13+
local l f
14+
l="$(normalize "$label")"
15+
f="$(normalize "$flags")"
16+
17+
if [[ "$id" == "0" ]]; then echo "owner"; return; fi
18+
if echo "$l $f" | grep -qE 'managed|work|profile_managed|ismanagedprofile'; then echo "work"; return; fi
19+
if echo "$l $f" | grep -qE 'secure folder|securefolder|knox'; then echo "secure"; return; fi
20+
echo "user"
21+
}
22+
23+
echo "== Raw: cmd user list =="
24+
RAW_CMD="$(adb shell cmd user list 2>/dev/null || true)"
25+
echo "$RAW_CMD"
26+
echo
27+
28+
echo "== Raw: dumpsys user (top) =="
29+
RAW_DUMP="$(adb shell dumpsys user 2>/dev/null || true)"
30+
echo "$RAW_DUMP" | sed -n '1,220p'
31+
echo
32+
33+
# Build a merged view primarily from dumpsys user because it carries flags/parents.
34+
# We parse blocks that contain "UserInfo{<id>:<name>:<serial>}" and try to capture flags and parentId.
35+
# Output columns:
36+
# user_id | type | name | flags | parent_id
37+
#
38+
# Note: parsing is best-effort; Android OEMs vary.
39+
40+
# Extract candidate user lines from dumpsys
41+
mapfile -t USER_LINES < <(
42+
echo "$RAW_DUMP" \
43+
| sed -n 's/.*UserInfo{\([0-9]\+\):\([^:}]*\):\([0-9]\+\).*/\1|\2|\3/p' \
44+
| sort -n -t'|' -k1,1
45+
)
46+
47+
if [[ ${#USER_LINES[@]} -eq 0 ]]; then
48+
echo "!! Could not parse users from dumpsys user; falling back to cmd user list only." >&2
49+
# Fallback parse from cmd user list
50+
echo "$RAW_CMD" \
51+
| sed -n 's/.*UserInfo{\([0-9]\+\):\([^:}]*\).*/\1|\2/p' \
52+
| sort -n \
53+
| while IFS='|' read -r id name; do
54+
printf "u%-4s %-7s %s\n" "$id" "$(guess_type "$name" "" "$id")" "$name"
55+
done
56+
exit 0
57+
fi
58+
59+
# Function to find a likely flags line for a given user id from dumpsys (best-effort)
60+
get_flags_for_user() {
61+
local id="$1"
62+
# Look for "UserInfo{ID:" line and take nearby "flags=" occurrence
63+
echo "$RAW_DUMP" \
64+
| awk -v uid="$id" '
65+
$0 ~ "UserInfo\\{"uid":" {in=1; c=0}
66+
in==1 {print; c++; if (c>40) in=0}
67+
' \
68+
| sed -n 's/.*flags=\([^ ]*\).*/\1/p' \
69+
| head -n 1
70+
}
71+
72+
get_parent_for_user() {
73+
local id="$1"
74+
# Try to locate "profileGroupId=" or "parentId=" nearby
75+
echo "$RAW_DUMP" \
76+
| awk -v uid="$id" '
77+
$0 ~ "UserInfo\\{"uid":" {in=1; c=0}
78+
in==1 {print; c++; if (c>40) in=0}
79+
' \
80+
| sed -n 's/.*profileGroupId=\([-0-9]\+\).*/\1/p; s/.*parentId=\([-0-9]\+\).*/\1/p' \
81+
| head -n 1
82+
}
83+
84+
printf "%-6s %-8s %-28s %-18s %-10s\n" "USER" "TYPE" "NAME" "FLAGS" "PARENT"
85+
printf "%-6s %-8s %-28s %-18s %-10s\n" "----" "----" "----" "-----" "------"
86+
87+
for L in "${USER_LINES[@]}"; do
88+
UID="${L%%|*}"
89+
REST="${L#*|}"
90+
NAME="${REST%%|*}"
91+
FLAGS="$(get_flags_for_user "$UID")"
92+
PARENT="$(get_parent_for_user "$UID")"
93+
[[ -z "$FLAGS" ]] && FLAGS="-"
94+
[[ -z "$PARENT" ]] && PARENT="-"
95+
TYPE="$(guess_type "$NAME" "$FLAGS" "$UID")"
96+
97+
printf "u%-5s %-8s %-28s %-18s %-10s\n" "$UID" "$TYPE" "$NAME" "$FLAGS" "$PARENT"
98+
done
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/usr/bin/env zsh
2+
3+
set -euo pipefail
4+
5+
# pull-apks-by-user.sh
6+
# Usage:
7+
# ./pull-apks-by-user.sh <search-term> [output-dir]
8+
#
9+
# Examples:
10+
# ./pull-apks-by-user.sh filemacros
11+
# ./pull-apks-by-user.sh com.virtualize ./tab-s9-apks
12+
#
13+
# Notes:
14+
# - Attempts to pull APK dirs per Android user/profile.
15+
# - Some profiles (e.g., Samsung Secure Folder / Knox) may deny shell access; those get skipped.
16+
17+
SEARCH_TERM="${1:-}"
18+
OUT_ROOT="${2:-./pulled-apks}"
19+
20+
if [[ -z "$SEARCH_TERM" ]]; then
21+
echo "Usage: $0 <search-term> [output-dir]" >&2
22+
exit 2
23+
fi
24+
25+
command -v adb >/dev/null 2>&1 || { echo "adb not found in PATH" >&2; exit 1; }
26+
27+
mkdir -p "$OUT_ROOT"
28+
29+
safe_name() {
30+
local s="$1"
31+
echo "$s" | tr -c 'A-Za-z0-9._-' '_'
32+
}
33+
34+
# Best-effort user type guesser using the label from cmd user list
35+
guess_user_type() {
36+
local label="$1"
37+
local id="$2"
38+
39+
# Normalize
40+
local l
41+
l="$(echo "$label" | tr '[:upper:]' '[:lower:]')"
42+
43+
if [[ "$id" == "0" ]]; then
44+
echo "owner"
45+
return
46+
fi
47+
48+
if echo "$l" | grep -qE 'work|managed|profile'; then
49+
echo "work"
50+
return
51+
fi
52+
53+
if echo "$l" | grep -qE 'secure folder|securefolder|knox'; then
54+
echo "secure"
55+
return
56+
fi
57+
58+
# Common Samsung patterns sometimes show "UserInfo{150:...}" without clear label
59+
echo "user"
60+
}
61+
62+
echo "==> Output dir: $OUT_ROOT"
63+
echo "==> Search term: $SEARCH_TERM"
64+
echo
65+
66+
# Get list of users: lines like "UserInfo{0:Owner:13} running"
67+
USER_LIST_RAW="$(adb shell cmd user list 2>/dev/null || true)"
68+
69+
if [[ -z "$USER_LIST_RAW" ]]; then
70+
echo "!! Failed to read user list (adb shell cmd user list)." >&2
71+
exit 1
72+
fi
73+
74+
# Parse user ids + labels
75+
# Extract: id and label inside UserInfo{<id>:<label>:...}
76+
lines="$(echo "$USER_LIST_RAW" \
77+
| sed -En 's/.*UserInfo{([0-9]+):([^:}]*).*/\1|\2/p' \
78+
| sort -n)"
79+
USERS=("${(f)lines}")
80+
81+
if [[ ${#USERS[@]} -eq 0 ]]; then
82+
echo "!! Could not parse any users from cmd user list output:" >&2
83+
echo "$USER_LIST_RAW" >&2
84+
exit 1
85+
fi
86+
87+
echo "==> Detected users:"
88+
for U in "${USERS[@]}"; do
89+
USER_ID="${U%%|*}"
90+
ULABEL="${U#*|}"
91+
UTYPE="$(guess_user_type "$ULABEL" "$USER_ID")"
92+
echo " - u$USER_ID ($UTYPE) label='$ULABEL'"
93+
done
94+
echo
95+
96+
TS_GLOBAL="$(date +%Y%m%d-%H%M%S)"
97+
98+
# For each user, try to list packages and filter by SEARCH_TERM
99+
for U in "${USERS[@]}"; do
100+
USER_ID="${U%%|*}"
101+
ULABEL="${U#*|}"
102+
UTYPE="$(guess_user_type "$ULABEL" "$USER_ID")"
103+
104+
echo "------------------------------------------------------------"
105+
echo "==> Scanning user u$USER_ID ($UTYPE) label='$ULABEL'"
106+
107+
# Listing packages per user can be blocked for some users (Secure Folder/Knox)
108+
# Capture stderr to detect SecurityException.
109+
LIST_OUT="$(
110+
adb shell pm list packages --user "$USER_ID" 2>&1 || true
111+
)"
112+
113+
if echo "$LIST_OUT" | grep -q 'SecurityException'; then
114+
echo "!! Skipping u$USER_ID ($UTYPE): shell lacks permission (SecurityException)."
115+
continue
116+
fi
117+
118+
if [[ -z "$LIST_OUT" ]]; then
119+
echo "!! Skipping u$USER_ID ($UTYPE): no output from pm list packages."
120+
continue
121+
fi
122+
123+
pkg_lines="$(echo "$LIST_OUT" \
124+
| sed 's/^package://g' \
125+
| grep -i -- "$SEARCH_TERM" \
126+
| sort -u)"
127+
PACKAGES=("${(f)pkg_lines}")
128+
129+
if [[ ${#PACKAGES[@]} -eq 0 ]]; then
130+
echo "No matches in u$USER_ID."
131+
continue
132+
fi
133+
134+
echo "Matched ${#PACKAGES[@]} package(s) in u$USER_ID:"
135+
printf ' - %s\n' "${PACKAGES[@]}"
136+
echo
137+
138+
for PKG in "${PACKAGES[@]}"; do
139+
echo "==> Resolving paths for $PKG (u$USER_ID)"
140+
141+
# pm path per user can also be blocked even if list packages worked, so guard it.
142+
PATH_OUT="$(
143+
adb shell pm path --user "$USER_ID" "$PKG" 2>&1 || true
144+
)"
145+
146+
if echo "$PATH_OUT" | grep -q 'SecurityException'; then
147+
echo "!! Cannot read paths for $PKG in u$USER_ID: SecurityException"
148+
continue
149+
fi
150+
151+
if ! echo "$PATH_OUT" | grep -q '^package:'; then
152+
echo "!! No package paths returned for $PKG in u$USER_ID"
153+
continue
154+
fi
155+
156+
apk_lines="$(echo "$PATH_OUT" | sed 's/^package://g' | sed 's/\r$//g')"
157+
APK_PATHS=("${(f)apk_lines}")
158+
159+
FIRST_PATH="${APK_PATHS[0]}"
160+
INSTALL_DIR="$(echo "$FIRST_PATH" | sed 's#/[^/]*\.apk$##')"
161+
162+
if [[ -z "$INSTALL_DIR" ]]; then
163+
echo "!! Failed to deduce install dir for $PKG in u$USER_ID"
164+
continue
165+
fi
166+
167+
TS="$(date +%Y%m%d-%H%M%S)"
168+
PKG_SAFE="$(safe_name "$PKG")"
169+
OUT_DIR="$OUT_ROOT/${PKG_SAFE}_${UTYPE}_u${USER_ID}_${TS}"
170+
mkdir -p "$OUT_DIR"
171+
172+
echo "==> Pulling install dir:"
173+
echo " $INSTALL_DIR"
174+
echo "==> -> $OUT_DIR"
175+
176+
# Pull the whole dir (base + splits). This is the installed code location.
177+
adb pull "$INSTALL_DIR" "$OUT_DIR" >/dev/null
178+
179+
# Metadata snapshot
180+
{
181+
echo "package=$PKG"
182+
echo "user_id=$USER_ID"
183+
echo "user_label=$ULABEL"
184+
echo "user_type=$UTYPE"
185+
echo "install_dir=$INSTALL_DIR"
186+
echo "pulled_at=$TS"
187+
echo
188+
echo "== pm path --user $USER_ID =="
189+
echo "$PATH_OUT"
190+
echo
191+
echo "== dumpsys package (summary) =="
192+
adb shell dumpsys package "$PKG" | sed -n '1,160p' | sed 's/\r$//g' || true
193+
echo
194+
echo "== user list =="
195+
echo "$USER_LIST_RAW"
196+
} > "$OUT_DIR/metadata.txt"
197+
198+
echo "==> Done: $PKG (u$USER_ID)"
199+
echo
200+
done
201+
done
202+
203+
echo "============================================================"
204+
echo "Done. Output in: $OUT_ROOT"
205+
echo "Timestamp base: $TS_GLOBAL"

zsh/platform/detect-platform.zsh

100644100755
File mode changed.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
<key>ServiceIPC</key>
1919
<false/>
2020
</dict>
21-
</plist>
21+
</plist>

0 commit comments

Comments
 (0)