ghostscript executable search path (with graphicsmagick)

147 Views Asked by At

Ealier I had both ghostscript and graphicsmagick installed via apt install ..., but to support the latest versions I decided to uninstall and instead build and install from source

Before everything worked, but after installing from source graphicsmagick commands that require ghostscript fails (when running from nginx)

Command

gm convert -density 50 input.pdf[0] -background white -resize 140x140 -strip -quality 40 thumb.jpg

The above command works fine when running it as root, but when running it from the webserver nginx an error is returned

execvp failed, errno = 2 (No such file or directory) gm convert: "gs" "-q" "-dBATCH" "-dSAFER" "-dMaxBitmap=50000000" "-dNOPAUSE" "-sDEVICE=pnmraw" "-dTextAlphaBits=4" "-dGraphicsAlphaBits=4" "-r50x50" "-dFirstPage=1" "-dLastPage=1" "-sOutputFile=/tmp/gmhjqkzP" "--" "/tmp/gmZaShhP" "-c" "quit". gm convert: Request did not return an image.

What is the reason for this and how to fix it?

Build and install (from source)

# cd /var/bin && wget https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10021/ghostscript-10.02.1.tar.gz && tar -xvf ghostscript-10.02.1.tar.gz
# cd ghostscript-10.02.1 && ./configure && make -j $(nproc) && make install

update

# type gs
gs is hashed (/usr/local/bin/gs)

The path /usr/local/bin is already included in /etc/profile

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin

When printing the PATH from nginx (HTTP request)

echo shell_exec('echo $PATH');

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

gs permission

enter image description here

2

There are 2 best solutions below

4
VonC On BEST ANSWER

You want:

+-----------------+     calls    +------------------+
|                 |  ----------> |                  |
|  nginx (web)    |              |  graphicsmagick  |
|                 | <----------  |                  |
+-----------------+   requires   +------------------+
                            |
                            |
                            V
                    +-----------------+
                    |                 |
                    |  ghostscript    |
                    |   (from source) |
                    +-----------------+

But graphicsmagick does not find ghostscript (mentioned in "Installing GraphicsMagick") when run by the nginx user, even though the gs path is included in PATH in /etc/profile.

Since nginx runs as a specific user (usually nginx or www-data), this user might not have the same PATH as the root user.
To check the PATH available to the nginx user, you can modify a script run by nginx to include echo $PATH > /tmp/nginx_path.txt. That will print the PATH seen by nginx to a file.

If the path to gs is /usr/local/bin/gs, you can modify the startup script of nginx to include this path. For instance:

# Edit nginx startup script
sudo nano /etc/init.d/nginx

# Find the section where PATH is set and modify it
# For example:
# PATH=/usr/local/bin:$PATH

Or, update the script that calls graphicsmagick:

#!/bin/bash
# Set the PATH
export PATH="/usr/local/bin:$PATH"

# Then call your graphicsmagick command
gm convert -density 50 input.pdf[0] -background white -resize 140x140 -strip -quality 40 thumb.jpg

As a workaround, specify the full path to the ghostscript executable in your graphicsmagick command.

gm convert -density 50 input.pdf[0] -background white -resize 140x140 -strip -quality 40 \
   -command "/usr/local/bin/gs" thumb.jpg

Make sure the nginx user has execute permissions on the ghostscript binary.
And check that any security policies or apparmor profiles are not restricting access to the necessary paths for the nginx user.


The OP clarkk adds in the comments:

you can configure/build graphicsmagick with frozen paths and then you neither need the PATH nor to manually pass the full path to gs... ./configure --with-frozenpaths.

That option makes sure GraphicsMagick remembers the locations of external utilities like ghostscript (gs) during its build process, eliminating the need to set the PATH environment variable or manually pass the full path to gs in your commands.

That method is particularly useful when dealing with environments like that of nginx, where setting environment variables can be challenging.

./configure --with-frozenpaths
make
sudo make install

This is similar to this option:

--with-frozenpaths

Enable frozen delegate paths.

Normally, external program names are substituted into the delegates.xml configuration file without full paths.

Specify this option to enable saving full paths to programs using locations determined by configure.

This useful for environments where programs are stored under multiple paths, and users may use different PATH settings than the person who builds ImageMagick.

18
Mark Setchell On

The problem is that the nginx user does not have Ghostscript (i.e. the gs binary) in their PATH, whereas you and the root user do. So the nginx user cannot find gs, and fails to exec it.

The first thing to do is su to, or login as, root (or any user who can run ghostscript), then run:

type gs

and it will tell you the full path to Ghostscript. Then you need to ensure that the nginx user has the directory containing gs on their PATH.

Another way to find the full path to Ghostscript is with:

sudo find / -type f -name "gs" 2> /dev/null

Alternatively, you can alter the ImageMagick "delegates" file to include the full path to the Ghostscript executable. So, you need to find the delegates.xml that ImageMagick is using like this:

identify -list configure | grep CONFIGURE_PATH

and that will tell you something like this, though it will be different on debian since I am using homebrew on a Mac:

CONFIGURE_PATH /opt/homebrew/Cellar/imagemagick/7.1.1-21/etc/ImageMagick-7/

Then you need to add delegates.xml onto that PATH, to give something akin to:

/opt/homebrew/Cellar/imagemagick/7.1.1-21/etc/ImageMagick-7/delegates.xml

Then, after backing up that file, edit it and change where it says delegate decode=pdf so that it has the full path to ghostscript there for all users. It looks like this:

<delegate decode="pdf" encode="ps" mode="bi" command="&quot;gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=ps2write&quot; &quot;-sPDFPassword=%a&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;"/>

So, you would need to change it to something more like this with the full path to gs:

<delegate decode="pdf" encode="ps" mode="bi" command="&quot;/FULL/PATH/TO/gs&quot; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &quot;-sDEVICE=ps2write&quot; &quot;-sPDFPassword=%a&quot; &quot;-sOutputFile=%o&quot; &quot;-f%i&quot;"/>