Accessing current username in sudo scripts on CentOS

I got a bit of a puzzle at work today.  I had a script that was executed as another user via sudo, but I wanted to access the original username in the script, to know who was executing it.  Sudoers manual suggest working with “Defaults env_keep“.  Looking into the /etc/sudoers, I noticed that $USERNAME variable was whitelisted (in line #3 below):

Defaults env_reset
Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS"
Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"

So, I tried to use the $USERNAME variable in my script but it was coming up with empty results.  That made me look deeper into default Bash initialization, and I found out that $USERNAME variable setup wasn’t a part of it.  However, $LOGNAME was (in /etc/profile).  I think, so few people actually use it that nobody noticed or bothered about it until now.  Anyway, the solution now was obvious – simply add $LOGNAME variable to the sudo white list.  Appending this line to the above env_keep ones did the job:

Defaults    env_keep += "LOGNAME"

There. In hopes it will help future generations…

P.S.: All that happened on a more or less default installation of CentOS 6.3, but I’m sure other Red Hat based distributions have a similar issue.

P.P.S.: If your script is ALWAYS invoked via sudo, also have a look at $SUDO_UID, $SUDO_GID, and $SUDO_USER variables.

Color builder for 256-color terminal

Color builder for 256-color terminal

With most Linux terminals now supporting 256 colors, a tool like this one is mighty useful in building the escape sequences.  For off-line use, all code is available in GitHub repository, and all logic behind the calculations is explained in this Habrahabr post (albeit, in Russian).

Color Bash Prompt

Color Bash Prompt

This wiki page is quite extensive, covering, among other things:

  • 256 color terminals
  • Regular, bold, underline fonts
  • High and low intensity
  • Prompt escape codes
  • Cursor positioning!
  • Return value visualization and error output
  • Shortening of current work path to N characters

Managing gettext translations on the command line

I am working on a rather multilingual project in the office currently.  And, as always, we tried a few alternatives before ending up with gettext again.  For those of you who don’t know, gettext is the de facto standard for managing language translations in software, especially when it comes to messages and user interface elements.  It’s a nice, powerful system but it’s a bit awkward when things come to web development.

Anyways, we started using it in a bit of a rush, without doing all the necessary planning, and quite soon ended up in a bit of a mess.  Different people used different editors to update translations.  And each person’s environment was setup in a different way.   All that made its way into the PO files that hold translations.  More so, we didn’t really define the procedure for the updates of translations.  That became a bigger problem when we realized that Arabic has only 50 translated strings, while English has 220, and Chinese 350.  All languages were supposed to have exactly the same amount of strings, even if the actual translations were missing.

So today I had to rethink and redefine how we do it.  First of all, I had to figure out and try the process outside of the project.  It took me a good couple of hours to brush up my gettext knowledge and find some useful documentation online.  Here is a very helpful article that got me started.

After reading the article, a few manuals and playing with the actual commands, I decided on the following:

  1. The source of all translations will be a single POT file.  This file will be completely dropped and regenerated every time any strings are updated in the source code.
  2. Each language will have a PO file of its own. However, the strings for the language won’t be extracted from the source code, but from the common POT file.
  3. All editors will use current project folder as the primary path.  In other words, “.” instead of full path to “/var/www/foobar”.  This will make all file references in PO/POT files point to a relative location to the project folder, ignoring the specifics of each contributor’s setup.
  4. Updating language template files (PO) and building of MO files will be a part of the project build/deploy script, to make sure everything stays as up to date as possible.

Now for the actual code.   Here is the shell script that does the job. (Here is a link to the Gist, just in case I’ll update it in the future.)

#!/bin/bash

DOMAIN="project_tag"
POT="$DOMAIN.pot"
LANGS="en_US ru_RU"
SOURCES="*.php"

# Create template
echo "Creating POT"
rm -f $POT
xgettext \
 --copyright-holder="2012 My Company Ltd" \
 --package-name="Project Name" \
 --package-version="1.0" \
 --msgid-bugs-address="translations@company.com" \
 --language=PHP \
 --sort-output \
 --keyword=__ \
 --keyword=_e \
 --from-code=UTF-8 \
 --output=$POT \
 --default-domain=$DOMAIN \
 $SOURCES

# Create languages
for LANG in $LANGS
do
 if [ ! -e "$LANG.po" ]
 then
 echo "Creating language file for $LANG"
 msginit --no-translator --locale=$LANG.UTF-8 --output-file=$LANG.po --input=$POT
 fi

echo "Updating language file for $LANG from $POT"
 msgmerge --sort-output --update --backup=off $LANG.po $POT

echo "Converting $LANG.po to $LANG.mo"
 msgfmt --check --verbose --output-file=$LANG.mo $LANG.po
done

 

Now, all you need to do is run the script once to get the default POT file and a PO file for every language.  You can edit PO files with translations for as much as you want.  Then simply run the script again and it will update generated MO files.  No parameters, no manuals, no nothing.  If you need to add another language, just put the appropriate locale in the $LANGS variable and run the script again.  You are good to go.

Enjoy!