Why Easytag Can Change Audacious’ Skin

I am a pedant and I like if my audiofiles have correct homogenous tags and names. I dislike missing album or artist field and filenames like “01 – Track 01”. That’s why I adore Easytag (http://projects.gnome.org/easytag/) – a robust tag editor with tasty features like tag encoding conversion or retrieving tags from filenames.

Screenshot - 12022012 - 12:02:38 PMI found only one problem in Easytag. It allows to open the selected file with an audio player. The thing is that if you use Audacious, then it will occasionally switch to its default skin.

Screenshot - 12022012 - 12:08:08 PM

For example, I have chosen Classic 1.3 skin. When I select Run Audio Player, my Audacious reverts to Default skin.

Aud Skin dialogSeveral tests shown that this problem happens only with archived skins, so, it was definitely a sign that the problem was in Audacious itself.

Easytag allows to provide a command line to launch audio player, so let’s set it as audacious --verbose -p and run Easytag from a terminal to see debug output produced by Audacious.

That’s what I was looking for:

pluginenum.c:142 [plugin_load]: Loading plugin: /usr/lib64/audacious/General/skins.so.
plugin-registry.c:483 [plugin_register_loaded]: Loaded plugin: /usr/lib64/audacious/General/skins.so
ui_skin.c:145 [active_skin_load]: /usr/share/audacious/Skins/Classic1.3.zip
ui_skin.c:931 [skin_load_nolock]: Attempt to load skin "/usr/share/audacious/Skins/Classic1.3.zip"
ui_skin.c:946 [skin_load_nolock]: Attempt to load archive
util.c:377 [archive_decompress]: Attempt to execute "unzip >/dev/null -o -j "/usr/share/audacious/Skins/Classic1.3.zip" -d /tmp/audacious.XXw2EDC1"
util.c:381 [archive_decompress]: could not execute cmd unzip >/dev/null -o -j "/usr/share/audacious/Skins/Classic1.3.zip" -d /tmp/audacious.XXw2EDC1
ui_skin.c:948 [skin_load_nolock]: Unable to extract skin archive (/usr/share/audacious/Skins/Classic1.3.zip)
ui_skin.c:1049 [skin_load]: loading failed
ui_skin.c:149 [active_skin_load]: loading failed
ui_skin.c:423 [init_skins]: Unable to load skin (/usr/share/audacious/Skins/Classic1.3.zip), trying default...
ui_skin.c:145 [active_skin_load]: /usr/share/audacious/Skins/Default
ui_skin.c:931 [skin_load_nolock]: Attempt to load skin "/usr/share/audacious/Skins/Default"

As far as we know, Audacious has a highly modular architecture, that’s why it has a special skins plugin. This plugin runs unzip subprocess to unpack the skin archive and switches to Default skin if it has encountered problems.

But… what problems? Let’s check up the output directory:

$ ls /tmp/audacious.XXw2EDC1
balance.png   eq_ex.png   main.png      nums_ex.png   pledit.png  posbar.png   skin-classic.hints  text.png      viscolor.txt
cbuttons.png  eqmain.png  monoster.png  playpaus.png  pledit.txt  shufrep.png  skin.hints          titlebar.png  volume.png

So, unzip had done his job, but Audacious complained! It’s time to look at skin plugin source in hope that it will throw light.

$ cd audacious-plugins-3.2.2/src/skins
$ find . -type f -exec grep -B 4 -A 3 -nH -e "could not execute cmd" {} +
./util.c-377-    AUDDBG("Attempt to execute \"%s\"\n", cmd);
./util.c-378-
./util.c-379-    if (system(cmd) != 0)
./util.c-380-    {
./util.c:381:        AUDDBG("could not execute cmd %s\n", cmd);
./util.c-382-        g_free(cmd);
./util.c-383-        return NULL;
./util.c-384-    }

Now we see that Audacious calls system function from standard C library. According to the manual,

system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed… The value returned is -1 on error (e.g., fork(2) failed), and the return status of the command otherwise.

Still no ideas. Let’s look at system implementation. We can find it in glibc-2.15/sysdeps/posix/system.c. It’s quite straightforward. system manipulates SIGINT and SIGQUIT handling, calls fork and waitpid to retrieve the command exit status.

fork was successful since archive was really unzipped. What problems can happen in waitpid?

ECHILD (for wait()) The calling process does not have any unwaited-for
children.

ECHILD (for waitpid() or waitid()) The process specified by pid (wait‐
pid()) or idtype and id (waitid()) does not exist or is not a
child of the calling process. (This can happen for one’s own
child if the action for SIGCHLD is set to SIG_IGN. See also the
Linux Notes section about threads.)

EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was
caught; see signal(7).

EINVAL The options argument was invalid.

This can happen for one's own child if the action for SIGCHLD is set to SIG_IGN looks suspicious. Grep it!

$ cd easytag-2.1.7
$ find . -type f -exec grep -B 1 -A 1 -nH -e "SIG_IGN" {} +
./src/easytag.c-171-    // Must handle this signal to avoid zombie of applications executed (ex: xmms)
./src/easytag.c:172:    signal(SIGCHLD,SIG_IGN); // Fix me! : can't run nautilus 1.0.6 with "Browse Directory With"
./src/easytag.c-173-#endif

We are on the right way! Easytag sets SIGCHLD handler to SIG_IGN, that’s why its child processes do not become zombies. But Audacious inherits this SIG_IGN behavior (the manual says that child processes inherit such signal handlers from parents) and cannot perform a successful waitpid. That’s why system function returns failure even for a successful unzipping.

It’s very easy to fix the bug. The problem is in Easytag, not in Audacious. It’s enough to set a normal handler for SIGCHLD. The handler will be called each time when a child process exits. This process resides in zombie state. The handler simply calls wait (this friend of waitpid waits for _any_ child process) thus removing zombie’s control block and freeing kernel resources that were still used by the zombie:

static void sigchld_handler(int signum)
{
       wait(NULL);
}

static void setup_sigchld()
{
       struct sigaction sa = {0};
       sa.sa_handler = sigchld_handler;
       sigemptyset(&sa.sa_mask);
       sa.sa_flags = SA_RESTART;
       sigaction(SIGCHLD, &sa, NULL);
}

Now replace signal(SIGCHLD,SIG_IGN) with setup_sigchld(), compile, and run Easytag – and any archived skin will be rendered without problems!

Advertisements

One response to “Why Easytag Can Change Audacious’ Skin

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s