Bash Scripting: Array Sorting and Safe Remote Power Off

Verified

Added on  2019/09/22

|7
|3564
|331
Report
AI Summary
The assignment is to create a script that runs as root and creates an alias 'poweroff' for the regular poweroff executable file. The goal is to automate this step by editing the appropriate bash startup scripts that run when root logs in, so that typing 'poweroff' does what it's supposed to do in all four scenarios: local and remote root, local and remote non-root. Additionally, the script should ensure that when connected remotely as root or non-root, the alternative code prescribed for bringing down the machine actually works.
tabler-icon-diamond-filled.svg

Contribute Materials

Your contribution can guide someone’s learning journey. Share your documents today.
Document Page
(1) Using arrays for sorting
Arrays are useful "vessels" for holding values being sorted. You can load an array with values you
want to sort, then sort them in place within the array using the bubble sort algorithm. This
assignment is for the purpose of practicing array handling-- creating arrays, writing values into their
elements, reading values back, measuring arrays' length, adding new elements to them, etc. The
bubble sort algorithm is secondary, so it's spelled out below in the instructions.
The exercise to perform:
Write a script that will accept the name of a file on the command line, load the file's words into an
array, sort them with the bubble sort algorithm, and output the sorted result. Put all the logic into a
function. Have the main program do nothing but receive from the command line, as arguments, the
items to be sorted; then call the function, passing those items to it. The function will load them into
an array and then proceed to sort.
Remember the bubble sort algorithm? In sorting a list of items it compares adjacent list items and
sorts them as a pair. That is, if they are already in order (first is "lesser" than second) nothing
happens, but if out of order (first is "greater" than second) it swaps or transposes them (they
exchange places). It goes through the list from beginning to end, swapping wherever necessary as it
goes. At that point, whatever element in the list is greatest will be at the end of the list. It performs
another pass, but this time doesn't treat all the pairs in the list; it omits the last pair since it's already
right. On the third pass it omits the last two pairs. On the nth pass it omits the last n-1 pairs.
Algorithm
In the main program:
- call the function, giving a list of arguments on its command line which are the words found in the file
In the function:
- assign the incoming arguments to an array, so each argument individually gets its own array element
- determine the number of elements (i.e., the length of the array). The number of element pairs is one less than the number of elements.
- run an outer loop a fixed number of iterations, that number being the number of pairs. Run this loop's counter downward, from
number-of-pairs through zero.
- within it run another loop, also with a fixed number of iterations. That number should be the outer loop's counter value. Run the inner
loop counter upward, from zero through one less than the current outer-loop-counter.
- within the inner loop, test the array element that has current inner-loop-counter as index against its "upward" neighbor, the one whose
index is one greater than the current inner-loop-counter. The test is whether it's greater. If it is, swap them.
- after the looping is done, at the bottom of your function, output the array. It will be sorted.
This worked for me. You might depart from it in some details as long as you are effecting a bubble
sort.
tabler-icon-diamond-filled.svg

Secure Best Marks with AI Grader

Need help grading? Try our AI Grader for instant feedback on your assignments.
Document Page
Submit your finished program to me and I will try it on a couple of my files. Please name it "sorter-
bubble" and place it in your assignments directory on the class server.
------------------------
Excerpts about handling arrays from bash man page:
In the context where an assignment statement is assigning a value to
a shell variable or array index, the += operator can be used to
append to or add to the variable's previous value.... When += is
applied to an array variable using compound assignment (see Arrays
below), the variable's value is not unset (as it is when using =),
and new values are appended to the array beginning at one greater
than the array's maximum index (for indexed arrays) or added as
additional key-value pairs in an associative array. ...
Arrays
Bash provides one-dimensional indexed and associative array vari-
ables. Any variable may be used as an indexed array; the declare
builtin will explicitly declare an array. There is no maximum limit
on the size of an array, nor any requirement that members be indexed
or assigned contiguously. Indexed arrays are referenced using inte-
gers (including arithmetic expressions) and are zero-based; asso-
ciative arrays are referenced using arbitrary strings.
An indexed array is created automatically if any variable is
assigned to using the syntax name[subscript]=value. The subscript
is treated as an arithmetic expression that must evaluate to a num-
ber. If subscript evaluates to a number less than zero, it is used
as an offset from one greater than the array's maximum index (so a
subcript of -1 refers to the last element of the array). To explic-
itly declare an indexed array, use declare -a name (see SHELL
BUILTIN COMMANDS below). declare -a name[subscript] is also
accepted; the subscript is ignored.
Associative arrays are created using declare -A name.
Attributes may be specified for an array variable using the declare
and readonly builtins. Each attribute applies to all members of an
array.
Arrays are assigned to using compound assignments of the form
name=(value1 ... valuen), where each value is of the form [sub-
script]=string. Indexed array assignments do not require the
bracket and subscript. When assigning to indexed arrays, if the
optional brackets and subscript are supplied, that index is assigned
to; otherwise the index of the element assigned is the last index
assigned to by the statement plus one. Indexing starts at zero.
When assigning to an associative array, the subscript is required.
This syntax is also accepted bythe declare builtin. Individual
array elements may be assigned to using the name[subscript]=value
syntax introduced above.
Any element of an array may be referenced using ${name[subscript]}.
The braces are required to avoid conflicts with pathname expansion.
If subscript is @ or *, the word expands to all members of name.
These subscripts differ onlywhen the word appears within double
quotes. If the word is double-quoted, ${name[*]} expands to a sin-
gle word with the value of each array member separated by the first
character of the IFS special variable, and ${name[@]} expands each
Document Page
element of name to a separate word. When there are no array mem-
bers, ${name[@]} expands to nothing. If the double-quoted expansion
occurs within a word, the expansion of the first parameter is joined
with the beginning part of the original word, and the expansion of
the last parameter is joined with the last part of the original
word. This is analogous to the expansion of the special parameters
* and @ (see Special Parameters above). ${#name[subscript]} expands
to the length of ${name[subscript]}. If subscript is * or @, the
expansion is the number of elements in the array. Referencing an
array variable without a subscript is equivalent to referencing the
array with a subscript of 0.
An array variable is considered set if a subscript has been assigned
a value. The null string is a valid value.
The unset builtin is used to destroy arrays. unset name[subscript]
destroys the array element at index subscript. Care must be taken
to avoid unwanted side effects caused by pathname expansion. unset
name, where name is an array, or unset name[subscript], where sub-
script is * or @, removes the entire array.
The declare, local, and readonly builtins each accept a -a option to
specify an indexed array and a -A option to specify an associative
array. If both options are supplied, -A takes precedence. The read
builtin accepts a -a option to assign a list of words read from the
standard input to an array. The set and declare builtins display
array values in a way that allows them to be reused as assignments.
(2) safe remote poweroff
If you run a root command shell on your linux machine and remotely administer another one, also via a root command shell, it's a matter of
time before you power off the local one only to discover that, oops, no, you just shut down the remote instead by accident. At that point a
remote physical presence is needed, to press the power button. Get in the car, or buy your plane ticket, or embarrass yourself with another
phone call to somebody onsite asking them to again fix your mistake for you. Or, you could write some substitute prophylactic code under the
same name(s) as the command(s) you habitually use for turning the machine off (probably shutdown, poweroff, or halt). Write it so it deflects
(does not actually run) the poweroff attempt, instead printing a helpfully alarmist message reminding the adminstrator that OMG he appears to
be trying to turn off the remote machine, telling him how to do so if it's what he actually intended, and exiting. Never again will he do it
unawares.
Let's assume the command you always use to put your machines down is poweroff. There is probably more than one solution. But any of them
will involve emplacing some code that goes by the name "poweroff" and gets called ahead of the usual code that actually turns out the lights,
eclipsing it. The new code will not turn the machine off, but rather print a message saying so and giving the user an alternative direct
command that will run the regular poweroff. Except, that's a bit of overkill if done unconditionally. You really want to take that step only in
case of a remote connection. If "poweroff" is issued on a local one, you want to let it go through. It's OK, the protection is unneeded, because
turning off the machine by accident is recoverable by just turning it right back on again. By contrast a remote user doesn't have that luxury; so
the new code should be conditional on whether the user's connection is local or remote.
The assignment to perform:
Operate as root. Write a script to be run in whenever the root user types "poweroff" in place of the regular poweroff. The script logic is to be:
determine if the connection is on a regular tty # (how? tty, ps, test, $, $0 might help; see Advanced Bash Scripting Guide
sec 36.1) if "tty"
print a message on screen saying in effect "You're local, I'm gonna turn you off now"
pull the trigger by running the regular poweroff (machine turns off)
else
print a warning message on screen declaring which machine this is by hostname and
printing an alternative command formulation the user could use
that would pull trigger by running the regular poweroff
exit with error (exit status value 1) (machine does not turn off)
endif
Document Page
Put your code in a file named saferemotepoweroff. Make it rootexecutable. Determine the directory where the poweroff executable file sits.
Then determine a directory that will be placed in root's path, whether root connects locally or remotely, and that occurs within PATH earlier
than poweroff's directory. You will have to determine this either analytically or empirically. If analytically, look into the bash startup files that
run in the 2 possible cases (remote root, local root) and see what the PATH variable gets set to. If empirically (easier), just log in both ways
and examine the results (i.e., the content of the PATH variables you get). Find a directory that shows up in both. Put your script file into it.
Note that there is no need to use your script for nonroot users. They need no protection from running the real poweroff, because it does its own
checking and powers off only if being run as root.
Manually create an alias for your your script, under the name "poweroff". This gives it prior precedence over running the regular executable
file by that name. That code turns off the machine; yours doesn't. (Another possibility would be to make a function out of your code, since
functions too trump executable files.) Automate this step to make it persistent, by editing the appropriate bash startup script(s) that fires off
whenever root logs in. You need to identify a startup script that runs both when connecting locally and remotely.
Connect all 4 ways local and remote root, local and remote nonroot and make sure typing "poweroff" does what it's supposed to in each. Do
this all on a local linux machine. Where you need a remote connection, simulate/achieve it by ssh'ing or telneting from your machine to itself.
Make sure that in both root cases, your new code gets called. But also make sure that in those two cases the alternative code prescribed to the
user to bring it down actually does do so. Make sure that in both local cases, "poweroff" does run the regular poweroff executable, and it prints
its refusal, "poweroff: Need to be root". Any time the machine goes down but you need to continue this exercise, reboot it.
Here's what should happen when you are connected remotely and try to power off, as nonroot and as root:
Perform this exercise at home. When you have it working please:
1 drop a copy of it into your assignments subdirectory, and
2 bring your file to class and set your classroom machine up the same way, so that it works and can be examined.
For reference:
This assignment calls on you to know
the basics of execution precedence among different types of code that have the same name
which shell startup scripts run under what scenarios
how to construct an "if" conditional
how to detect if you are local or remote
Below are a couple of summary slides and the man page section about bash invocation, for your reference.
tabler-icon-diamond-filled.svg

Secure Best Marks with AI Grader

Need help grading? Try our AI Grader for instant feedback on your assignments.
Document Page
INVOCATION
A login shell is one whose first character of argument zero is a
‐, or one started with the ‐‐login option.
An interactive shell is one started without non‐option arguments
and without the ‐c option whose standard input and error are
both con‐
nected to terminals (as determined by isatty(3)), or one
started with the ‐i option. PS1 is set and $‐ includes i if
bash is inter‐
active, allowing a shell script or a startup file to test
this state.
The following paragraphs describe how bash executes its
startup
files. If any of the files exist but cannot be read, bash
reports
an error. Tildes are expanded in file names as described
below under Tilde Expansion in the EXPANSION section.
When bash is invoked as an interactive login shell, or as a
non‐
interactive shell with the ‐‐login option, it first reads and
exe‐
Document Page
cutes commands from the file /etc/profile, if that file
exists.
After reading that file, it looks for ~/.bash_profile,
~/.bash_login, and ~/.profile, in that order, and reads and
executes
commands from the first one that exists and is readable. The
‐‐noprofile option may be used when the shell is started to
inhibit this behavior.
When a login shell exits, bash reads and executes commands from
the
files ~/.bash_logout and /etc/bash.bash_logout, if the files
exists.
When an interactive shell that is not a login shell is started,
bash
reads and executes commands from ~/.bashrc, if that file
exists.
This may be inhibited by using the ‐‐norc option. The ‐‐rcfile
file
option will force bash to read and execute commands from
file instead of ~/.bashrc.
When bash is started non‐interactively, to run a shell script,
for example, it looks for the variable BASH_ENV in the
environment,
expands its value if it appears there, and uses the expanded
value as the name of a file to read and execute. Bash behaves
as if the
following command were executed:
if [ ‐n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for
the file name.
If bash is invoked with the name sh, it tries to mimic the
startup behavior of historical versions of sh as closely as
possible, while conforming to the POSIX standard as well. When
invoked as an inter‐ active login shell, or a non‐
interactive shell with the ‐‐login
option, it first attempts to read and execute commands
from
/etc/profile and ~/.profile, in that order. The ‐‐noprofile
option may be used to inhibit this behavior. When invoked as
an interac‐
tive shell with the name sh, bash looks for the variable
ENV,
expands its value if it is defined, and uses the expanded value
as
the name of a file to read and execute. Since a shell invoked as
sh
does not attempt to read and execute commands from any other
startup files, the ‐‐rcfile option has no effect. A non‐
interactive shell invoked with the name sh does not attempt to
read any other startup files. When invoked as sh, bash enters
posix mode after the startup files are read.
When bash is started in posix mode, as with the ‐‐posix command
line
option, it follows the POSIX standard for startup files. In
this mode, interactive shells expand the ENV variable and
commands are
read and executed from the file whose name is the expanded
value. No other startup files are read.
Bash attempts to determine when it is being run with its
standard
input connected to a network connection, as when executed by
the
remote shell daemon, usually rshd, or the secure shell daemon
sshd.
If bash determines it is being run in this fashion, it reads
and executes commands from ~/.bashrc, if that file exists and
is read‐
able. It will not do this if invoked as sh. The ‐‐norc option
may be used to inhibit this behavior, and the ‐‐rcfile option
may be
Document Page
used to force another file to be read, but rshd does not
generally invoke the shell with those options or allow them to
be specified.
If the shell is started with the effective user (group) id not
equal
to the real user (group) id, and the ‐p option is not supplied,
no
startup files are read, shell functions are not inherited from
the
environment, the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE
vari‐
ables, if they appear in the environment, are ignored, and
the
effective user id is set to the real user id. If the ‐p option
is
supplied at invocation, the startup behavior is the same, but
the
effective user id is not reset.
‐‐ bash man page
chevron_up_icon
1 out of 7
circle_padding
hide_on_mobile
zoom_out_icon
[object Object]

Your All-in-One AI-Powered Toolkit for Academic Success.

Available 24*7 on WhatsApp / Email

[object Object]