Run an alias based on output of another command - terminal

My laptop for option to switch fan speeds. So to change fan speed when it is plugged in, i want to write a alias to to detect if it is plugged in and check the fan speed accordingly.
To check if battery is plugged in, i have used command
acpi -a
which gives output of either off-line or online so based on this, i want to execute my alias.
Aliases I have created to switch fan speed is
alias fan-turbo='cd /sys/devices/platform/asus-nb-wmi; sudo sh -c "echo 1 >> fan_boost_mode"; sudo sh -c "echo 1 >> throttle_thermal_policy"; source ~/.bashrc;'
My approach was to get output and search for off-line using Grep, but don't know how to proceed.

Related

How can I start an ssh session with a script without redirecting stdin?

I have a series of bash commands, some with interactive prompts, that I need run on a remote machine. I have to have them called in a certain order for different scenarios, so I've been trying to make a bash script to automate the process for me. However, it seems like every way to start an ssh session with a bash script results in the the redirection of stdin to whatever string or file was used to initiate the script in the first place.
Is there a way I can specify that a certain script be executed on a remote machine, but also forward stdin through ssh to the local machine to enable the user to interact with any prompts?
Here's a list of requirements I have to clarify what I'm trying to do.
Run a script on a remote machine.
Somewhere in the middle of that remote script be command that will prompt for input. Example: git commit will bring up vim.
If that command is git commit and it brings up vim, the user should be able to interact with vim as if it was running locally on their machine.
If that command prompts for a [y/n] response, the user should be able to input their answer.
After the user enters the necessary information—by quitting vim or pressing return on a prompt—the script should continue to run like normal.
My script will then terminate the ssh session. The end product is that commands were executed for the user without them needing to be aware that it was through a remote connection.
I've been testing various different methods with the following script that I want run on the remote machine.
#!/bin/bash
echo hello
vim
echo goodbye
exit
It's crucial that the user be able to use vim, and then, when the user finishes, "goodbye" should be printed to the screen and the remote session should be terminated.
I've tried uploading a temporary script to the remote machine and then running ssh user#host bash /tmp/myScript, but that seems to also take over stdin completely, rendering it impossible to let the user respond to prompts for user input. I've tried adding the -t and -T options (I'm not sure if they're different), but I still get the same result.
One commenter mentioned using expect, spawn, and interact, but I'm not sure how to use those tools together to get my desired behavior. It seems like interact will result in the user gaining control over stdin, but then there's no way to have it relinquished once the user quits vim in order to let my script continue execution.
Is my desired behavior even possible?
Ok, I think I've found my problem. I was creating a wrapper script for ssh that looked like this:
#!/bin/bash
tempScript="/tmp/myScript"
remote=user#host
commands=$(</dev/stdin)
cat <(echo "$commands") | ssh $remote "cat > $tempScript && chmod +x $tempScript" &&
ssh -t $remote $tempScript
errorCode=$?
ssh $remote << RM
if [[ -f $tempScript ]]; then
rm $tmpScript
fi
RM
exit $errorCode
It was there that I was redirecting stdin, not ssh. I should have mentioned this when I formulated my question. I read through that script over and over again, but I guess I just overlooked that one line. Removing that line totally fixed my problem.
Just to clarify, changing my script to the following totally fixed my problem.
#!/bin/bash
tempScript="/tmp/myScript"
remote=user#host
commands="$#"
cat <(echo "$commands") | ssh $remote "cat > $tempScript && chmod +x $tempScript" &&
ssh -t $remote $tempScript
errorCode=$?
ssh $remote << RM
if [[ -f $tempScript ]]; then
rm $tmpScript
fi
RM
exit $errorCode
Once I changed my wrapper script, my test script described in the question worked! I was able to print "hello" to the screen, vim appeared and I was able to use it like normal, and then once I quit vim "goodbye" was printed and the ssh client closed.
The commenters to the question were pointing me in the right direction the whole time. I'm sorry I only told part of my story.
I've searched for solutions to this problem several times in the past, however never finding a fully satisfactory one. Piping into ssh looses your interactivity. Two connects (scp/ssh) is slower, and your temporary file might be left lying around. And the whole script on the command line often ends up in escaping hell.
Recently I encountered that the command line buffer size is usually quite large (getconf ARG_MAX > 2MB where I looked). And this got me thinking about how I could use this and mitigate the escaping issue.
The result is:
ssh -t <host> /bin/bash "<(echo "$(cat my_script | base64 | tr -d "\n")" | base64 --decode)" <arg1> ...
or using a here document and cat:
ssh -t <host> /bin/bash $'<(cat<<_ | base64 --decode\n'$(cat my_script | base64)$'\n_\n)' <arg1> ...
I've expanded on this idea to produce a fully working BASH example script sshx that can run arbitrary scripts (not just BASH), where arguments can be local input files too, over ssh. See here.

How to drop root privileges from a POSIX shell script?

What portable options do exist to drop root privileges and execute a given command as a different user from a shell script?
After doing a bit of research, here are a few non-options:
su $USER -c "$COMMAND" uses the PAM stack and creates a new cgroup (when run under systemd). It also fails in user namespaces, because the audit call returns -EPERM on older versions of Linux.
sudo -u $USER $COMMAND is not installed by default on many systems.
start-stop-daemon --pidfile /dev/null --start --chuid $USER --startas /bin/sh -- -c "$COMMAND" is very hard to use and only available on Debian systems.
chpst -u $USER $COMMAND is missing on many systems.
runuser -u $USER -- $COMMAND works where su doesn't, but requires recent util-linux.
If it's POSIX you want, then su is your only option (unless you want to write a C program). su has several advantages (or not, depending on your requirements):
It's a system tool which isn't going to forget about the new coffee UID introduced in Linux 3.42 (the UID for beverage drinking purposes) and which isn't going to goof by dropping user privileges before group privileges or to forget about capabilities.
It sets privileges to a known state: a user ID, that user's recorded group(s) from the user and group databases, no extra capabilities.
It records log entries.
And, again, it's completely standard, guaranteed to be available everywhere but on the most broken systems.
Now in practice some systems aren't POSIX — like this older Linux where it fails in user namespaces. Them's the breaks.
If you want something that's reasonably portable in practice (on non-embedded platforms) and that gives you a greater decree of control, use Perl (or Python, a bit less commonly installed). For preference, use a solid module: Privilege::Drop.
perl -e 'use Privileges::Drop; drop_uid_gid(123, 456); exec("/path/to/command", "--option", "an argument")'
Privilege::Drop takes care of doing things right (dropping supplemental groups, checking for errors). It might not be complete, however; for example it isn't aware of capabilities.
If you must do it by hand, take care of several things:
Drop group privileges before user privileges.
To drop supplemental groups, set $) = "456 456" where 456 is the target GID ($) = 456 would only set the EGID without affecting the supplemental groups).
Check the (E)[UG]ID afterwards and abort on failure.
For folks who are reading this in a context where the relevant meaning of "POSIX shell script" is "POSIX sh script, being run on an arbitrary Linux system", as opposed to "sh script, run on a system guaranteed to have only tools guaranteed by POSIX", there are more options available.
Borrowing from an excellent answer to the UNIX & Linux question How do I drop root privileges in shell scripts:
Modern util-linux has setpriv, which can be used in the manner of:
setpriv --reuid=user --regid=group --init-groups --inh-caps=-all yourcommand
The excellent article by #JdeBP Don't Abuse su For Dropping Privileges is also worth reading.

Automate xmodem file upload

I have a device where a software update is done by uploading a file through xmodem.
I was able to do this in two ways using the lrzsz (http://ohse.de/uwe/software/lrzsz.html) package.
1) Using screen
screen /dev/tty.myserialdevice 115200
and then
^A:exec !! sz -X file.bin
or
2) Using cu
cu -l /dev/tty.myserialdevice -s 115200
and then
~+lsz -X file.bin
Now I would like to write a script (preferably a ruby or a bash script) to automate the file upload.
Is there a way to pipe the file into screen or cu or use standard input?
When I tried to write a script I only got to the point of opening a connection.
But even then I had problems to properly close this connection again.
Not sure if you are still having this problem, but I just recently had to piece together something for a similar situation.
I used screen to automate this portion, as it has some command line options to run in the background. First, I create a detached screen session, like this:
screen -d -m /dev/tty.myserialdevice 115200
Then, I can reference my created session with the -p0 option, and give it a command to execute with -X, which in this case is a command to screen itself to start an xmodem file transfer.
screen -p0 -X exec \!\! sz -X filetosend
I had noticed in other places on the web that .\!\! was also sometimes used, so keep this in mind to try if you still have problems.

Why ftam service will start and return prompt from terminal but not from bash script?

I am starting ftam server (ft820.rc on CentOS 5) using bash version bash 3.0 and I am having an issue with starting it from the script, namely in the script I do
ssh -nq root#$ip /etc/init.d/ft820.rc start
and the script won't continue after this line, although when I do on the machine defined by $ip
/etc/init.d/ft820.rc start
I will get the prompt back just after the service is started.
This is the code for start in ft820.rc
SPOOLPATH=/usr/spool/vertel
BINPATH=/usr/bin/osi/ft820
CONFIGFILE=${SPOOLPATH}/ffs.cfg
# Set DBUSERID to any value at all. Just need to make sure it is non-null for
# lockclr to work properly.
DBUSERID=
export DBUSERID
# if startup requested then ...
if [ "$1" = "start" ]
then
mask=`umask`
umask 0000
# startup the lock manager
${BINPATH}/lockmgr -u 16
# update attribute database
${BINPATH}/fua ${CONFIGFILE} > /dev/null
# clear concurrency locks
${BINPATH}/finit -cy ${CONFIGFILE} >/dev/null
# startup filestore
${BINPATH}/ffs ${CONFIGFILE}
if [ $? = 0 ]
then
echo Vertel FT-820 Filestore running.
else
echo Error detected while starting Vertel FT-820 Filestore.
fi
umask $mask
I repost here (on request of #Patryk) what I put in the comments on the question:
"is it the same when doing the ssh... in the commandline? ie, can you indeed connect without entering a password, using the pair of private_local_key and the corresponding public_key that you previously inserted in the destination root#$ip:~/.ssh/authorized_keys file ? – Olivier Dulac 20 hours ago "
"you say that, at the commandline (and NOT in the script) you can ssh root#.... and it works without asking for your pwd ? (ie, it can then be run from a script?) – Olivier Dulac 20 hours ago "
" try the ssh without the '-n' and even without -nq at all : ssh root#$ip /etc/init.d/ft820.rc start (you could even add ssh -v , which will show you local (1:) and remote (2:) events in a very verbose way, helping in knowing where it gets stuck exactly) – Olivier Dulac 19 hours ago "
"also : before the "ssh..." line in the script, make another line with, for example: ssh root#ip "set ; pwd ; id ; whoami" and see if that works and shows the correct information. This may help be sure the ssh part is working. The "set" part will also show you the running shell (ex: if it contains BASH= , you're running bash. Otherwise SHELL=... should give a good hint (sometimes not correct) about which shell gets invoked) – Olivier Dulac 19 hours ago "
" please try without the '-n' (= run in background and wait, instead of just run and then quit). It it doesn't work, try adding -t -t -t (3 times) to the ssh, to force it to allocate a tty. But first, please drop the '-n'. – Olivier Dulac 18 hours ago "
Apparently what worked was to add the -t option to the ssh command. (you can go up to put '-t -t -t' to further force it to try to allocate the tty, depending on the situation)
I guess it's because the invoked command expected to be run within an interactive session, and so needed a "tty" to be the stdout
A possibility (but just a wild guess) : the invoked rc script outputs information, but in a buffered environment (ie, when not launched via your terminal), the calling script couldn't see enough lines to fill the buffer and start printing anything out (like when you do a "grep something | somethings else" in a buffered environment and ctrl+c before the buffer was big enough to display anything : you end up thinking no lines were foudn by the grep, whereas there was maybe a few lines already in the buffer). There is tons to be said about buffering, and I am just beginning to read about it all. forcing ssh to allocate a tty made the called command think it was outputting to a live terminal session, and that may have turned off the buffering and allowed the result to show. Maybe in the first case, it worked too, but you could never see the output?

How to Profile a C-Program in a Loop of Shell Script, Using Shark on Mac?

I have a question about Shark-profiling on mac.
Say if I have a C-program, compiled with:
gcc -o mycprog mycprog.c -g -pg
and also I have a shell script something like:
for file in ($SomeDirectory)
do
mycprog $file
done
I need to profile the average performance for all files in $SomeDirectory.
Where should I put the shark -i command? Thanks.
This is not a great approach, for various reasons - ideally you should modify your program's outer loop so that it can process all the files on the command line, then you can just do everything in one run:
$ shark -i
$ mycprog $SomeDirectory/*
If you can't do that then you will need to set your Shark configuration for system-wide profiling and start profiling before your bash loop and stop profiling afterwards. When you subsequently view the profile in Shark you'll be able to filter out the processes that you're not interested in.
Firstly open Shark (the GUI app), set up your configuration and enable remote control (Sampling => Programmatic). Make sure you have Time Profile and Everything selected.
Then from the command line:
$ chudRemoteCtrl -s "mycprog"
$ sleep 1
$ for f in $SomeDirectory/*
$ do
$ mycprog $SomeDirectory/$f
$ done
$ chudRemoteCtrl -e

Resources