1 Introduction
2 Interface
sudo?
current-sudo
subprocess/ sudo
system*/ sudo
system*/ exit-code/ sudo
process*/ sudo
process*/ ports/ sudo
3 Known Issues
4 History
5 Legal
Version: 1:0

sudo: Sudo Command Processes in Racket

Neil Van Dyke

 (require (planet neil/sudo:1:0))

1 Introduction

This package adds automatic sudo functionality to the Racket standard procedures for creating processes on Unix-like systems. This can be used for running commands as different user accounts, such as for running privileged programs for system administration.
The procedures of this package wrap the Racket standard procedures, to automatically use programs like sudo and gksudo when necessary. There are features for specifying which programs to try, in which order.
For example, say you’re writing a system administration program in Racket, which needs to call the do-stuff program, which can only be run as user root. Instead of using the Racket system* procedure, you use system*/sudo, like so:
(system*/sudo "/sbin/do-stuff"
              "remove"
              ".*java.*"
              "costing"
              ">"
              "$0")
When Bob runs this Racket program on a system into which he has only an SSH command-line interface, logged in as user bob, that system*/sudo call might effectively behave like:
(system* "/usr/bin/sudo"
         "-u"
         "root"
         "/sbin/do-stuff"
         "remove"
         ".*java.*"
         "costing"
         ">"
         "$0")
As an aside, system*/sudo actually found the sudo program in "/usr/bin/sudo"; it would have looked a couple other likely places, if not found there. For security reasons, it does not search the user’s own executable search path, however.
Later, when Bob runs the Racket program when logged in as user root, that same system*/sudo call will not require a sudo, and instead be like:
(system* "/sbin/do-stuff"
         "remove"
         ".*java.*"
         "costing"
         ">"
         "$0")
When Bob later runs the same Racket program on his workstation, as user bob, where he’s using a graphical desktop, that same system*/sudo call might use the GUI gksudo program:
(system* "/usr/local/bin/gksudo"
         "-u"
         "root"
         "/sbin/do-stuff remove \".*java.*\" costing \">\" \"\\$0\"")
This is a good time to mention a restriction that this package imposes: notice in the example above that gksudo takes a single string as the command line to run, rather than the safer individual arguments that are not re-parsed. This package has to use quoting and escaping to sanitize the command line for re-parsing. Additionally, to avoid potential problems in, say, shell scripts that might be called by gksudo, this package limits commands and arguments used with gksudo to contain only printable ASCII characters in the range 32 to 126. This prevents things like newlines that might be handled improperly in shell scripts, and multi-byte character encodings that might defeat quoting and escaping.
This package also supports users other than root, with the #:user optional keyword argument to each procedure. For example, to start a process running the PostgreSQL psql program as user postgres, you might do something like:
(subprocess/sudo #:user "postgres"
                 #f
                 #f
                 #f
                 "/usr/bin/psql"
                 "-h"
                 my-database-host
                 "-d"
                 my-database-name)
Note: Even a properly administered sudo configuration can make it easier to compromise the reliability of security of a system, accidentally or intentionally, in some cases. At the same time, judicious configuration and use of sudo can conceivably reduce accidents and vulnerabilites. (Ask any network administrator who, in lieu of sudo has had a root login window open, and accidentally hit the middle mouse button, pasting a pile shell script text, which was then executed happily and destructively as root.) Similarly, this package can be beneficial for reliability and security, but must be used judiciously.

2 Interface

The interface to this package consists mainly of a set of procedures that correspond to Racket standard process creation procedures, plus a parameter that controls how a sudo program is found, by default.

procedure

(sudo? x)  boolean?

  x : any/c
Predicate for permissible values of the current-sudo parameter, and of the #:sudo keyword argument of the various procedures. This value specifies how to select a sudo program whenever one is needed.
The values can be expressed by the contract:
(or/c 'sudo
      'gksudo
      absolute-path?
      (listof (or/c 'sudo
                    'gksudo
                    absolute-path?)))
More specifically, the value may be either one of the following, or a list of one or more of the following:
  • 'sudo – The normal sudo program, in one of a few conventional places, if the executable file exists.

  • 'gksudo – The gksudo program, in one of a few conventional places, if the executable file exists, and only if X is in use. Currently, X in use is determined by whether or not the DISPLAY environment variable is set.

  • An absolute path, which is used if the executable file exists there. It is then called with the syntax of normal sudo. Note that, since gksudo uses a different command line syntax than sudo, if you want to use gksudo, generally you must use the 'gksudo value instead of an absolute path.

When the value a list of the alternatives above, then they are tried in order, until one succeeds.

parameter

(current-sudo)  sudo?

(current-sudo val)  void?
  val : sudo?
Parameter for how to select a sudo program, if the #:sudo argument is not given. The default value is '(gksudo sudo), which means that, by default, gksudo will be used if it can be found and X is in use, and otherwise sudo will be used.

procedure

(subprocess/sudo [#:user user    
  #:sudo sudo]    
  stdout    
  stdin    
  stderr    
  command    
  arg ...)  any
  user : string? = "root"
  sudo : sudo? = (current-sudo)
  stdout : any/c
  stdin : any/c
  stderr : any/c
  command : absolute-path?
  arg : any/c
Wrapper for suprocess with sudo support.

procedure

(system*/sudo [#:user user    
  #:sudo sudo]    
  command    
  arg ...)  any
  user : string? = "root"
  sudo : sudo? = (current-sudo)
  command : absolute-path?
  arg : any/c
Wrapper for system* with sudo support.

procedure

(system*/exit-code/sudo [#:user user    
  #:sudo sudo]    
  command    
  arg ...)  any
  user : string? = "root"
  sudo : sudo? = (current-sudo)
  command : absolute-path?
  arg : any/c
Wrapper for system*/exit-code with sudo support.

procedure

(process*/sudo [#:user user    
  #:sudo sudo]    
  command    
  arg ...)  any
  user : string? = "root"
  sudo : sudo? = (current-sudo)
  command : absolute-path?
  arg : any/c
Wrapper for process* with sudo support.

procedure

(process*/ports/sudo [#:user user    
  #:sudo sudo]    
  out    
  in    
  error-out    
  command    
  arg ...)  any
  user : string? = "root"
  sudo : sudo? = (current-sudo)
  out : any/c
  in : any/c
  error-out : any/c
  command : absolute-path?
  arg : any/c
Wrapper for process*/ports with sudo support.

3 Known Issues

4 History

5 Legal

Copyright 2012 Neil Van Dyke. This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See http://www.gnu.org/licenses/ for details. For other licenses and consulting, please contact the author.