Archive for Bash Scripting

Custom sudo Login Prompt: Confuse Your Coworkers and Friends!

obey_sudo_card-p137691123849347174bh2r3_400

Quick way to have fun with Bash and sudo on a boring day. Insert the following into your /etc/bashrc, /etc/bash.bashrc, or similar file (as is appropriate for your distro and version):


alias sudo='sudo -p "Congratulations, %p! You are the one-millionth user to attempt to sudo to %U! Enter your password to see what you've won. "';

More info, from man sudo:

%H  expanded to the host name including the domain name (on if the machine's host name is fully qualified or the fqdn option is set in sudoers(5))
%h  expanded to the local host name without the domain name
%p  expanded to the name of the user whose password is being requested (respects the rootpw, targetpw and runaspw flags in sudoers(5))
%U  expanded to the login name of the user the command will be run as (defaults to root unless the -u option is also specified)
%u  expanded to the invoking user's login name
%%  two consecutive % characters are collapsed into a single % character
The prompt specified by the -p option will override the system password prompt on systems that support PAM unless the passprompt_override flag is disabled in sudoers.

Bash ‘for’ Loop and Filenames With Spaces

A quick post for my own future reference, primarily.

After banging me face off the desk for a while trying to figure out how to batch-convert a heaping spoonful of space-laden-named Excel files to CSV for a project I’m doing for my wife, I found a solution in the $IFS environment variable. Thus:

#!/bin/bash
IFSTMP=$IFS;
IFS=$(echo -en "\n\b");
for i in $(ls -1 *.xls); do
xls2csv $i > $i.csv 2>/dev/null;
done;
IFS=$IFSTMP;

Easy as pie. And I don’t mean like a mince pie or something that many folks don’t even like, but like convincing toddler to eat a chocolate cream pie. Yeah, that easy.

Add New Domains to Apache On Command Line

I had a need to add several domains to Apache for a client about an hour ago, and instead of copying, pasting, changing, and repeating, I opted to script it. It’s very simple (read: not secure for multiple users in production), but served my needs today beautifully. Feel free to clean it up and use it, change it, whatever.

The primary reason this is added is to show how CLI scripts can be made to wait for and accept user input. Note the use of STDOUT and STDIN in the script. This is something that I’ve found to be quite unfortunately overlooked in a lot of CLI scripts. Using this, you can make a PHP CLI script interactive, instead of requiring arguments to be passed on the command line.

Also, note that this particular script wasn’t needed for user creation, DNS, or anything of the like. Strictly for adding an Apache directive for the domain.

(The code itself is indented, but I’m learning more and more why Philip Olsen scoffed at me for installing Serendipity last year. Eventually my error in judgment shall be rectified.)


#!/usr/bin/php
<?php

if(trim(`whoami`) != 'root') die("You must be root to run this command.\n");

fwrite(STDOUT,"Domain to be added: ");
$domain = trim(fgets(STDIN));

fwrite(STDOUT,"Username to use for this domain: ");
$user = trim(fgets(STDIN));

fwrite(STDOUT,"Group to use for this domain: ");
$group = trim(fgets(STDIN));

fwrite(STDOUT,"Path to use for the web root of this domain: ");
$path = trim(fgets(STDIN));

fwrite(STDOUT,"Server administrator's email address to use: ");
$email = trim(fgets(STDIN));

echo "Writing.... ";

$newHost =<<<TXT

NameVirtualHost *:80
<VirtualHost *:80>
ServerAdmin $email
DocumentRoot $path
ServerName $domain
ServerAlias www.$domain
ErrorLog logs/$domain-error_log
CustomLog logs/$domain-access_log combined
<IfModule mod_suphp.c>
suPHP_Engine On
suPHP_ConfigPath "/etc/"
suPHP_AddHandler x-httpd-php
suPHP_AddHandler php5-script .php
suPHP_UserGroup $user $group
</IfModule>
</VirtualHost>
TXT;

file_put_contents('/etc/httpd/conf/httpd.conf',$newHost,FILE_APPEND);

echo "Done.\n";

fwrite(STDOUT,"Should I restart Apache now for the changes to take effect? (y/N) ");
if(preg_match('/^ye?s?$/i',trim(fgets(STDIN)))) {
exec('service httpd restart');
echo "Apache restarted.\n";
}

?>

Finding Which php.ini Is Loaded

This is actually a throwback to the post Overriding PHP Settings, but is presented here separately due to the number of times the question is asked.

If you’re on a *NIX-based system and are trying to figure out which php.ini is being loaded by the CLI, simply run the following command:

ls -l `/bin/env php -i | grep "Loaded Configuration File" | sed 's/.*=> //g'`

If you want to edit the file right from that command, simply replace ‘ls -l’ with an editor of your choice, such as:

vi `/bin/env php -i | grep "Loaded Configuration File" | sed 's/.*=> //g'`

Script to Automate Multi-User Development Permissions In Linux: Part II

As discussed in the previous entry a few moments ago:

While deploying a large virtual server for a client, one of the obstacles they had faced with their previous setup was multi-user permissions with regard to everyone modifying files and directories with their own FTP and/or SSH accounts. Unfortunately, for reasons that won’t be divulged here (quite frankly because I don’t know), and even after I suggested alternatives such as updating policies and standard operating procedures, the client opted to continue using previous methods, but still wanted a resolution to issues they faced in their multi-developer environment.

The following simple Bash script is called directly via a cron job every fifteen minutes:


#!/bin/bash

mode='4771';

# The directory in which "multiUserPermissionsFix" resides.
dir='/danbrown/scripts/';

if ( test "`whoami`" != 'root' ); then
echo "$0 MUST be run as root!";
exit -1;
fi;

if ( test "$1" == "" ); then
echo "You must specify the base directory.";
exit -1;
fi;

if ( test "$2" == "" ); then
echo "You must specify the group ownership to set.";
exit -1;
fi;

if [ -d "$1" ]; then

echo -n "Verifying ownership and group ownership of all files.... ";
chown -R $2:$2 $1;
echo "Done.";

cd $1;

echo -n "Resetting permissions now.... ";
find . -type d -exec chmod $mode '{}' \;
# for i in `find . -type d`; do
# chmod $mode $i;
# done;
echo "Done.";

echo -n "Verifying ownership requirements.... ";
find . -type d -exec chmod o+s '{}' \;
# for i in `find . -type d`; do
# chmod o+s $i;
# done;
echo "Good.";

echo -n "Verifying group requirements.... ";
find . -type d -exec chmod g+s '{}' \;
# for i in `find . -type d`; do
# chmod g+s $i >> /dev/null 2>&1
# done;
echo "Good.";

if [ "$?" != "0" ]; then
echo "That group does not exist!";
exit -1;
fi;

echo -n "Fixing bad permissions.... ";
$dir/multiUserPermissionsFix $1;
echo "Done.";

echo "Task Complete.";

exit 0;

else

echo "That directory doesn't exist!";
exit -1;

fi;

Script to Automate Multi-User Development Permissions In Linux: Part I

While deploying a large virtual server for a client, one of the obstacles they had faced with their previous setup was multi-user permissions with regard to everyone modifying files and directories with their own FTP and/or SSH accounts. Unfortunately, for reasons that won’t be divulged here (quite frankly because I don’t know), and even after I suggested alternatives such as updating policies and standard operating procedures, the client opted to continue using previous methods, but still wanted a resolution to issues they faced in their multi-developer environment.

To address one particular issue, I wrote the following very simple Bash script and set it on a cron running every 15 minutes, dumping all errors and standard output to a black hole.


#!/bin/bash
# Name: multiUserPermissionsFix
# Mode: 0755

function usage {
echo "USAGE: $0 base_directory";
exit -1;
}

# If we didn't even bother to pass an argument to this script, die.
if ( test "$1" == "" ); then
usage;
fi;

# Set up find correctly.
export IFS=$'\n';

# If this is a valid directory, do the work.
if [ -d "$1" ]; then

# Force a trailing slash - workaround for symlinked directories, etc.
d="$1/";

# Find and fix all file permissions --- but ignore all 'cache' cases.
find . -type f -not -path "*cache*" -exec chmod 0664 '{}' \;

exit 0;

# If it wasn't a valid directory passed to this script, die on an error.
else
echo "That directory does not exist!";
exit -1;
fi;

This script is actually not called directly, but instead by a parent script. It recursively runs through all files and sets the permissions to 0664 (owner read/write, group read/write, world read) so that users within the group can update or delete the files within the directory without requiring administrative intervention.

Overriding PHP Settings

Getting the following error?

Invalid command ‘php_flag’, perhaps mis-spelled or defined by a module not included in the server configuration

Well, if your host and server configuration allow it, and should you so desire, you can override some of the php.ini settings for your site (and each subdirectory within your web directory). While you can’t override PHP_INI_SYSTEM settings, there’s still a host of other settings that can be overridden while still maintaining the rest of the configuration of the system – if your server is set up to allow it. And no, n00bZ, it won’t override php.ini for the rest of the box.

To grab your php.ini and copy it to the current directory, just run this command:

cp `/bin/env php -i | grep "Loaded Configuration File" | sed 's/.*=> //g'` .

Then just edit the php.ini and save it in place. Voila! No need to restart Apache or anything.

Finding the Maximum Number of Command Line Characters

Whilst idling in EFNet #php.doc, Kalle brought up the question about maximum input length from the command line on Linux. It’s acceptable to presume that the maximum length is ~65536, as you might expect ((32 * 8)2). But how can one be sure?

A bit of code:


#!/bin/bash
i=0;
str="PHP";
len=0;
while(test "X`echo $str`" = "X$str") > /dev/null 2>1 && res=`expr "X$str" : ".*" 2>&1` && len=$res; do
i=`expr $i + 1`;
str=$str"X"$str;
done;
echo "Maximum characters allowed via command line: $len";

That’ll do, pig. That’ll do.