Substitute user mid-Bash-script & continue running commands (Mac OSX) - arrays

I'm building two scripts which combined will fully uninstall a program (Microsoft Lync) on Mac OS X. I need to be able to swap from an account with root access (this account initially executes the first script) to the user whom is currently logged in.
This is necessary because the second script needs to be executed not only by the logged-in user, but from said user's shell. The two scripts are name Uninstall1.sh and Uninstall2.sh in this example.
Uninstall1.sh (executed by root user):
#!/bin/bash
#commands ran by root user
function rootCMDs () {
pkill Lync
rm -rf /Applications/Microsoft\ Lync.app
killall cfprefsd
swapUser
}
function swapUser () {
currentUser=$(who | grep console | grep -v _mbsetupuser | grep -v root | awk '{print $1}' | head -n 1)
cp /<directory>/Uninstall2.sh${currentUser}
su -l ${currentUser} -c "<directory>/{currentUser}/testScript.sh";
<directory> actually declared in the scripts, but for the sake of privacy I've excluded it.
In the above script, I run some basic commands as the root user to remove the app to the trash, and kill cfprefsd to prevent having to reboot the machine. I then call the swapUser function, which dynamically identifies the current user account signed into and assigns this to the variable currentUser (in this case within our environment, it's safe to assume only one user is logged into the computer at a time). I'm not sure whether or not I'll need the cp directory/Uninstall2.sh portion yet, but this is intended to solve a different problem.
The main problem is getting the script to properly handle the su command. I use the -l flag to simulate a user login, which is necessary because this not only substitutes to the user account which is logged into, but it launches a new shell as said user. I need to use -l because OS X doesn't allow modifying another user's keychain from an admin account (the admin account in question has root access, but isn't nor does it switch to root). -c is intended to execute the copied script, which is as follows:
Uninstall2.sh (needs to be executed by the locally logged-in user):
#!/bin/bash
function rmFiles () {
# rm -rf commands
# rm -rf commands
certHandler1
}
function certHandler1 () {
myCert=($(security dump-keychain | grep <string> | grep alis | sed -e 's/"alis"<blob>="//' | sed -e 's/"//'))
cLen=${#myCert[#]} # Count the amount of items in the array; there are usually duplicates
for ((i = 0;
i < ${cLen};
i++));
do security delete-certificate -c ${myCert[$i]};
done
certHandler2
}
function certHandler2 () {
# Derive the name of, and delete Keychain items related to Microsoft Lync.
myAccount=$(security dump-keychain | grep KeyContainer | grep acct | sed -e 's/"acct"<blob>="//' | sed -e 's/"//')
security delete-generic-password -a ${myAccount}
lyncPW=$(security dump-keychain | grep Microsoft\ Lync | sed -e 's/<blob>="//' | awk '{print $2, $3}' | sed -e 's/"//')
security delete-generic-password -l "${lyncPW}"
}
rmFiles
In the above script, rmFiles kicks the script off by removing some files and directories from the user's ~/Library directory. This works without a problem, assuming the su from Uninstall1.sh properly executes this second script using the local user's shell.
I then use security dump-keychain to dump the local user's shell, find a specific certificate, then assign all results to the cLen array (because there may be duplicates of this item in a user's keychain). Each item in the array is then deleted, after which a few more keychain items are dynamically found and deleted.
What I've been finding is that the first script will either properly su to the logged-in user which it finds, at which point the second script doesn't run at all. Or, the second script is ran as the root user and thus doesn't properly delete the keychain items from the logged-in user it's supposed to su to.
Sorry for the long post, thanks for reading, and I look forward to some light shed on this situation!

Revision
I managed to find a way to achieve all that I am trying to do in a single bash script, rather than two. I did this by having the main script create another bash script in /tmp, then executing that as the local user. I'll provide it below to help anybody else whom may need this functionality:
Credit to the following source for the code on how to create another bash script within a bash script:
http://tldp.org/LDP/abs/html/here-docs.html - Example 19.8
#!/bin/bash
# Declare the desired directory and file name of the script to be created. I chose /tmp because I want this file to be removed upon next start-up.
OUTFILE=/tmp/fileName.sh
(
cat <<'EOF'
#!/bin/bash
# Remove user-local Microsoft Lync files and/or directories
function rmFiles () {
rm -rf ~/Library/Caches/com.microsoft.Lync
rm -f ~/Library/Preferences/com.microsoft.Lync.plist
rm -rf ~/Library/Preferences/ByHost/MicrosoftLync*
rm -rf ~/Library/Logs/Microsoft-Lync*
rm -rf ~/Documents/Microsoft\ User\ Data/Microsoft\ Lync\ Data
rm -rf ~/Documents/Microsoft\ User\ Data/Microsoft\ Lync\ History
rm -f ~/Library/Keychains/OC_KeyContainer*
certHandler1
}
# Need to build in a loop that determines the count of the output to determine whether or not we need to build an array or use a simple variable.
# Some people have more than one 'PRIVATE_STRING' certificate items in their keychain - this will loop through and delete each one. This may or may not be necessary for other applications of this script.
function certHandler1 () {
# Replace 'PRIVATE_STRING' with whatever you're searching for in Keychain
myCert=($(security dump-keychain | grep PRIVATE_STRING | grep alis | sed -e 's/"alis"<blob>="//' | sed -e 's/"//'))
cLen=${#myCert[#]} # Count the amount of items in the array
for ((i = 0;
i < ${cLen};
i++));
do security delete-certificate -c ${myCert[$i]};
done
certHandler2
}
function certHandler2 () {
# Derive the name of, then delete Keychain items related to Microsoft Lync.
myAccount=$(security dump-keychain | grep KeyContainer | grep acct | sed -e 's/"acct"<blob>="//' | sed -e 's/"//')
security delete-generic-password -a ${myAccount}
lyncPW=$(security dump-keychain | grep Microsoft\ Lync | sed -e 's/<blob>="//' | awk '{print $2, $3}' | sed -e 's/"//')
security delete-generic-password -l "${lyncPW}"
}
rmFiles
exit 0
EOF
) > $OUTFILE
# -----------------------------------------------------------
# Commands to be ran as root
function rootCMDs () {
pkill Lync
rm -rf /Applications/Microsoft\ Lync.app
killall cfprefsd # killing cfprefsd mitigates the necessity to reboot the machine to clear cache.
chainScript
}
function chainScript () {
if [ -f "$OUTFILE" ]
then
# Make the file in /tmp executable. This is necessary for /tmp as a non-root user cannot access files in this directory.
chmod 755 $OUTFILE
# Dynamically identify the user currently logged in. This may need some tweaking if multiple User Accounts are logged into the same computer at once.
currentUser=$(who | grep console | grep -v _mbsetupuser | grep -v root | awk '{print $1}' | head -n 1);
su -l ${currentUser} -c "bash /tmp/UninstallLync2.sh"
else
echo "Problem in creating file: \"$OUTFILE\""
fi
}
# This method also works for generating
#+ C programs, Perl programs, Python programs, Makefiles,
#+ and the like.
# Commence the domino effect.
rootCMDs
exit 0
# -----------------------------------------------------------
Cheers!

Related

Opening File with emacsclient from windows

I try to open a File from within Windows10 in an emacsclient running on wsl2/Debian.
Upon startup I launch wsl/debian, X410 as X-server and the emacs daemon.
I can start a new emacs frame with emacsclient with the following startclient.bat:
#echo off
debian.exe run "if [ -z \"$(pidof emacsclient)\" ]; then export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0; emacsclient -c; pkill '(gpg|ssh)-agent'; fi;"
Then I created a shortcut so I can open a new emacs frame from the taskbar. To my surprise it works quite smoothly... so far. However, I can not figure out how to open a file with the emacsclient from within windows explorer.
I guess I would need to pass an argument to the emacsclient -c in the bat file, but how do I do this?
EDIT:
by doing the following:
debian.exe run "if [ -z \"$(pidof emacsclient)\" ]; then export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0; emacsclient -c "%1"; pkill '(gpg|ssh)-agent'; fi;"
I can open a file with emacsclient with:
startclient.bat file.txt
However when using open with the path to the file is messed up
instead of
/mnt/c/Users/path/to/file
I get
/mnt/c/Windows/system32/C:\Users\path\to\file
How would I go about passing the correct path as variable?
Not quite sure where the /mnt/c/Windows/system32/ is coming from, but you can convert the C:\Users\path\to\file into /mnt/c/Users/path/to/file by surrounding the %1 argument with wslpath, as in something like:
emacsclient -c "$(wslpath '%1')"

Commands Working In Terminal, But Not In Bash Script

I'm sysadmin for couple of webservers deployed with cpanel hosting panel. I'm trying to finish up with a backup script. There are two commands bundled with Cpanel, that will be used in this script. These commands are;
1. whmapi1 modifyacct user=USERNAME BACKUP=[01]
This Command has booleans to set, what it does is either enable or
disable backup for a specific user.
2. /usr/local/cpanel/bin/backup --force
Once backup is enabled for a user/users, then this command starts the
backup process on the server.
So here is my script logic & the script.
#!/bin/bash
Arrays
L=($( comm -23 <(du -h --max-depth=1 /home 2>/dev/null | grep G | awk -F"/" '{print $NF}' | sort | egrep -vw '(home|virtfs)') <(ls -al /var/cpanel/suspended/ | grep -v 'lock' | sort) ))
Above Array contains all the account whose home directories have
exceeded 1GB limit.
S=($(comm -23 <(du -h --max-depth=1 /home 2>/dev/null | egrep -v '(!G|.cp|cP|clamav)' | awk -F"/" '{print $NF}' | sort | egrep -vw '(home|virtfs)') <(ls -al /var/cpanel/suspended/ | grep -v 'lock' | sort) ))
Above Array contains all the account whose home directories are less
than 1GB limit.
whmapi1 modifyacct user=${L[#]} BACKUP=0 && whmapi1 modifyacct user=${S[#]} BACKUP=0
Above command disables backup for all users for start, to start from
scracth.
whmapi1 modifyacct user=${S[#]} BACKUP=1
T
his command enables backup for all accounts whose home dirs are less
than 1 GB
/usr/local/cpanel/bin/backup --force
This command starts backup process for all enabled users.
The logic is, that I want to create backup of small accounts first, and then when it's finished, I'll run it for larger accounts.
PROBLEM: all commands execute successfully when run directly in terminal, but it doesn't when run via a script. Problem occurs at account enabling & disabling.
It either disables all or enables all, and not the partial accounts, as intended by the logic of the script.
Can anyone point out, where & what I'm missing?
Thanks in advance !!
${l[#]} exands to user1 user2 user3 ..., so user=${L[#]} expands to user=user1 user2 user3 ..., if you want to fun foreach user, you need to loop over users.
du_buff=$(du -h --max-depth=1 /home 2>/dev/null)
lock_buff=$(ls -al /var/cpanel/suspended/ | grep -v 'lock' | sort)
L=($(comm -23 <(echo "$du_buff" | grep G | awk -F"/" '{print $NF}' | sort | egrep -vw '(home|virtfs)') <(echo "$lock_buff") ))
S=($(comm -23 <(echo "$du_buff" | egrep -v '(!G|.cp|cP|clamav)' | awk -F"/" '{print $NF}' | sort | egrep -vw '(home|virtfs)') <(echo "$lock_buff") ))
# for every user in L and S
for user in "${L[#]}" "${S[#]}"; do
whmapi1 modifyacct user=$user BACKUP=0
done
# for every user in S
for user in "${S[#]}"; do
whmapi1 modifyacct user=$user BACKUP=1
done
/usr/local/cpanel/bin/backup --force

Copy SRC directory with adding a prefix to all c-library functions

I have a embedded C static library that communicates with a hardware peripheral. It currently does not support multiple hardware peripherals, but I need to interface to a second one. I do not care about code footprint rightnow. So I want to duplicate that library; one for each hardware.
This of course, will result in symbol collision. A good method is to use objcopy to add a prefix to object files. So I can get hw1_fun1.o and hw2_fun1.o. This post illustrates it.
I want to add a prefix to all c functions on the source level, not the object. Because I will need to modify a little bit for hw2.
Is there any script, c-preprocessor, tool that can make something like:
./scriptme --prefix=hw2 ~/src/ ~/dest/
I'll be grateful :)
I wrote a simple bash script that does the required function, or sort of. I hope it help someone one day.
#!/bin/sh
DIR_BIN=bin/ext/lwIP/
DIR_SRC=src/ext/lwIP/
DIR_DST=src/hw2_lwIP/
CMD_NM=mb-nm
[ -d $DIR_DST ] || ( echo "Destination directory does not exist!" && exit 1 );
cp -r $DIR_SRC/* $DIR_DST/
chmod -R 755 $DIR_DST # cygwin issue with Windows7
sync # file permissions. (Pure MS shit!)
funs=`find $DIR_BIN -name *.o | xargs $CMD_NM | grep " R \| T " | awk '{print $3}'`
echo "Found $(echo $funs | wc -w) functions, processing:"
for fun in $funs;
do
echo " $fun";
find $DIR_DST -type f -exec sed -i "s/$fun/hw2_$fun/g" {} \;
done;
echo "Done! Now change includes and compile your project ;-)"

Looking to take only main folder name within a tarball & match it to folders to see if it's been extracted

I have a situation where I need to keep .tgz files & if they've been extracted, remove the extracted directory & contents.
In all examples, the only top-level directory within the tarball has a different name than the tarball itself:
[host1]$ find / -name "*\#*.tgz" #(has an # symbol somewhere in the name)
/1-#-test.tgz
[host1]$ tar -tzvf /1-#-test.tgz | head -n 1 | awk '{ print $6 }'
TJ #(directory name)
What I'd like to accomplish (pulling my hair out; rusty scripting fingers), is to look at each tarball, see if the corresponding directory name (like above) exists. If it does, echo "rm -rf /directoryname" into an output file for review.
I can read all of the tarballs into an array ... but how to check the directories?
Frustrated & appreciate any help.
Maybe you're looking for something like this:
find / -name "*#*.tgz" | while read line; do
dir=$(tar ztf "$line" | awk -F/ '{print $6; exit}')
test -d "$dir" && echo "rm -fr '$dir'"
done
Explanation:
We iterate over the *#*.tgz files found with a while loop, line by line
Get the list of files in the tgz file with tar ztf "$line"
Since paths are separated by /, use that as the separator in the awk, print the 6th field. After the print we exit, making this equivalent to but more efficient than using head -n1 first
With dir=$(...) we put the entire output of the tar..awk chain, thus the 6th field of the first file in the tar, into the variable dir
We check if such directory exists, if yes then echo an rm command so you can review and execute later if looks good
My original answer used a find ... -exec but I think that's not so good in this particular case:
find / -name "*#*.tgz" -exec \
sh -c 'dir=$(tar ztf "{}" | awk -F/ "{print \$6; exit}");\
test -d "$dir" && echo "rm -fr \"$dir\""' \;
It's not so good because of running sh for every file, and since we are using {} in the subshell, we lose the usual benefits of a typical find ... -exec where special characters in {} are correctly handled.

Pass stdin into Unix host or dig command

Let's say I have a list of IPs coming into a log that I'm tailing:
1.1.1.1
1.1.1.2
1.1.1.3
I'd like to easily resolve them to host names. I'd like to be able to
tail -f access.log | host -
Which fails as host doesn't understand input from stdin in this way. What's the easiest way to do with without having to write a static file or fallback to perl/python/etc.?
Use xargs -l:
tail -f access.log | xargs -l host
You could also use the read builtin:
tail -f access.log | while read line; do host $line; done
In the commands below, replace cat with tail -f, etc. if needed.
Using host:
$ cat my_ips | xargs -i host {}
1.1.1.1.in-addr.arpa domain name pointer myhost1.mydomain.com.
1.1.1.2.in-addr.arpa domain name pointer myhost2.mydomain.com.
Using dig:
$ cat my_ips | xargs -i dig -x {} +short
myhost1.mydomain.com.
myhost2.mydomain.com.
Note that the -i option to xargs implies the -L 1 option.
To first get the IPs of one's host, see this answer.
In bash You can do:
stdout | (dig -f <(cat))
Example program:
(
cat <<EOF
asdf.com
ibm.com
microsoft.com
nonexisting.domain
EOF
) | (dig -f <(cat))
This way You only invoke 'dig' once.

Resources