[EnglishFrontPage] [TitleIndex] [WordIndex

<- Page 1: /usr/bin/lpr

/usr/dt/bin/Xsession

   1 #!/usr/bin/ksh
   2 # ##########################################################################
   3 #
   4 #   Xsession
   5 #
   6 #   Common Desktop Environment (CDE)
   7 #
   8 #   Configuration script for the Login Manager
   9 #
  10 #   (c) Copyright 1996 Digital Equipment Corporation.
  11 #   (c) Copyright 1993,1994,1996 Hewlett-Packard Company.
  12 #   (c) Copyright 1993,1994,1996 International Business Machines Corp.
  13 #   (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
  14 #   (c) Copyright 1993,1994,1996 Novell, Inc.
  15 #   (c) Copyright 1996 FUJITSU LIMITED.
  16 #   (c) Copyright 1996 Hitachi.
  17 #
  18 #       ************** DO NOT EDIT THIS FILE **************
  19 #
  20 #   /usr/dt/bin/Xsession is a factory-default file and will
  21 #   be unconditionally overwritten upon subsequent installation.
  22 #   Modification is discouraged.
  23 #
  24 #   $XConsortium: Xsession.src /main/20 1997/11/25 14:16:29 narayan $
  25 #
  26 #   Usage: $0 [-session session_name]
  27 #
  28 # ##########################################################################
  29 
  30 
  31 # 112
  32 
  33 # ##########################################################################
  34 #
  35 #
  36 #   This script starts the user's session. It searches for one of three
  37 #   types of startup mechanisms, in the following order:
  38 #
  39 #     DT     existence of CDE DT Session Manager on the system
  40 #     XDM    "$HOME/.xsession" (executable)
  41 #     xinit  "$HOME/.x11start"  (executable)
  42 #
  43 #   If none of these startup mechanisms exist, a default window manager
  44 #   and terminal emulator client are started.
  45 #
  46 # ##########################################################################
  47 
  48 #
  49 # Variables must be explicitly exported
  50 #
  51 set +a
  52 
  53 # ##########################################################################
  54 #
  55 #   Initialize session startup logging
  56 #
  57 # ##########################################################################
  58 
  59   exec >/dev/null 2>/dev/null
  60 
  61 # 147
  62 
  63   LOGDIR=$HOME/.dt
  64 
  65   LOGFILENAME=$LOGDIR/startlog
  66   MSGLOGFILENAME=$LOGDIR/errorlog
  67 
  68   if [ ! -d $LOGDIR ]; then
  69     mkdir $LOGDIR
  70     if [ -d $LOGDIR ]; then
  71       /usr/bin/chmod 755 $LOGDIR
  72     fi
  73   fi
  74 
  75   [ -f $LOGFILENAME.older ] && rm -f $LOGFILENAME.older
  76   [ -f $LOGFILENAME.old ] && mv -f $LOGFILENAME.old $LOGFILENAME.older
  77   [ -f $LOGFILENAME ] && mv -f $LOGFILENAME $LOGFILENAME.old
  78   touch $LOGFILENAME
  79   [ ! -f $MSGLOGFILENAME ] && touch $MSGLOGFILENAME
  80 
  81   if [ -w $LOGFILENAME ]; then
  82     exec >>$LOGFILENAME 2>&1
  83   fi
  84 
  85 
  86   Log()
  87   {
  88     echo "--- $1" >>$LOGFILENAME 2>&1
  89   }
  90 
  91   Log "$(date)"
  92   Log "$0 starting..."
  93 
  94 # ##########################################################################
  95 #
  96 #   Global environment section
  97 #
  98 #    DT pre-sets the following environment variables for each user.
  99 #
 100 #     (internal)
 101 #
 102 #     DISPLAY      set to the value of the first field in the Xservers file.
 103 #     HOME         set to the user's home directory (from /etc/passwd)
 104 #     LANG         set to the display's current NLS language (if any)
 105 #     LC_ALL       set to the value of $LANG
 106 #     LOGNAME      set to the user name
 107 #     PATH         set to the value of the Dtlogin "userPath" resource
 108 #     USER         set to the user name
 109 #     SHELL        set to the user's default shell (from /etc/passwd)
 110 #     TZ           set to the value of the Dtlogin "timeZone" resource
 111 #
 112 #
 113 #     (Xsession)
 114 #
 115 #     TERM         set to xterm
 116 #     EDITOR       set to the default editor
 117 #     KBD_LANG     set to the value of $LANG for certain languages
 118 #     MAIL         set to "/var/mail/$USER"
 119 #
 120 #
 121 #    Three methods are available to modify or add to this list depending
 122 #    on the desired scope of the resulting environment variable.
 123 #
 124 #     1. X server and/or all users on a display       (Xconfig file)
 125 #     2. all users on a display                       (Xsession file)
 126 #     3. individual users                             (.dtprofile file)
 127 #
 128 #    See DT on-line help, the DT Users Guide, or the Dtlogin(1X) man
 129 #    page for details on setting environment variables.
 130 #
 131 #
 132 # ##########################################################################
 133 
 134   [ -z "$EDITOR" ] && EDITOR=/usr/dt/bin/dtpad
 135   [ -z "$MAIL" ] && MAIL="/var/mail/$USER"
 136   [ -z "$LANG" ] && LANG="C"
 137   TERM=dtterm
 138   SESSION_SVR=`hostname`
 139 
 140   export PATH EDITOR MAIL TERM SESSION_SVR LANG
 141 
 142 # 256
 143 
 144   #
 145   # Set the keyboard language if necessary...
 146   #
 147   if [ ! -z "$LANG" ]
 148   then
 149       case $LANG in
 150             bulgarian | czech   | hebrew | hungarian | \
 151             japanese  | korean  | polish | rumanian  | \
 152             russian   | serbocroatian)
 153                         KBD_LANG=$LANG
 154                         export KBD_LANG;;
 155 
 156              chinese-t) KBD_LANG=t_chinese
 157                         export KBD_LANG;;
 158              chinese-s) KBD_LANG=s_chinese
 159                         export KBD_LANG;;
 160 
 161              *);;
 162      esac
 163   fi
 164 
 165 
 166   #
 167   # Locate configuration file directories
 168   #
 169   XDIR="/usr/bin/X11"
 170   DT_BINPATH=/usr/dt/bin
 171   DT_INSTALL_CONFIG=/usr/dt/config
 172   DT_CONFIG=/etc/dt/config
 173   DT_CONFIG_PATH="$DT_CONFIG $DT_INSTALL_CONFIG"
 174 
 175 # ##########################################################################
 176 #
 177 #  Default desktop component configuration variable settings
 178 #
 179 #  This section sets the default value for variables controlling
 180 #  some desktop components.
 181 #
 182 # ##########################################################################
 183 
 184   #
 185   # Input method server startup
 186   #
 187   if [ -z "$DTSTARTIMS" ]; then
 188      DTSTARTIMS=True
 189   fi
 190 
 191   if [ "$DTSTARTIMS" = "False" ]; then
 192      unset DTSTARTIMS
 193   fi
 194 
 195   #
 196   # Default desktop screen saver action list
 197   #
 198   export DTSCREENSAVERLIST="StartDtscreenSwarm StartDtscreenQix \
 199     StartDtscreenFlame StartDtscreenHop StartDtscreenImage StartDtscreenLife \
 200     StartDtscreenRotor StartDtscreenPyro StartDtscreenWorm StartDtscreenBlank"
 201 
 202   #
 203   # Session startup clients and args
 204   #
 205   if [ "$SESSIONTYPE" = "altDt" ]; then
 206       dtstart_session[0]="$SDT_ALT_SESSION"
 207       dtstart_hello[0]="$SDT_ALT_HELLO"
 208       dtstart_hello_info[0]="$SDT_ALT_HELLO"
 209   else
 210       DTSESSION_ARGS=""
 211       if [ $# -ge 2 ]; then
 212           if [ "$1" = "-session" ]; then
 213               DTSESSION_ARGS="$1 $2"
 214           fi
 215       fi
 216       dtstart_session[0]="$DT_BINPATH/dtsession $DTSESSION_ARGS"
 217       dtstart_hello[0]="$DT_BINPATH/dthello &"
 218       dtstart_hello_info[0]="$DT_BINPATH/dthello -file $INFO_PATH -file /etc/copyright &"
 219   fi
 220   dtstart_session[1]="$HOME/.xsession"
 221   dtstart_session[2]="$HOME/.x11start"
 222   dtstart_session[3]="$XDIR/xterm -geometry 80x24+10+10"
 223   dtstart_hello[1]="$XDIR/xsetroot -default &"
 224   dtstart_searchpath="$DT_BINPATH/dtsearchpath -ksh"
 225   dtstart_ttsession="$DT_BINPATH/ttsession -s"
 226   dtstart_dtdbcache="$DT_BINPATH/dtdbcache -init"
 227   #
 228   # dtdbcache file's directory should match
 229   # _DTDTSMMTEMPDIR in DtSvc/DtUtil1/DtsMM.h
 230   #
 231 
 232 
 233 
 234   dtdbcacherm="rm -f /tmp/dtdbcache_$DISPLAY"
 235 
 236   dtstart_appgather="$DT_BINPATH/dtappgather &"
 237 
 238 
 239 
 240 
 241   xdmstart_session[0]="$HOME/.xsession"
 242   xdmstart_session[1]="/usr/lib/X11/xdm/sys.xsession"
 243   xdmstart_session[2]="$XDIR/xterm -geometry 80x24+10+10 -ls"
 244   xdmstart_hello="$XDIR/xsetroot -default &"
 245 
 246 
 247   SESSIONLOGDIR=$LOGDIR/sessionlogs
 248   SESSIONLOGFILENAME="$SESSIONLOGDIR/$SESSION_SVR"_DISPLAY=$DISPLAY
 249 
 250   if [ ! -d $SESSIONLOGDIR ]; then
 251     mkdir $SESSIONLOGDIR
 252     if [ -d $SESSIONLOGDIR ]; then
 253       /usr/bin/chmod 755 $SESSIONLOGDIR
 254     fi
 255   fi
 256 
 257   touch $SESSIONLOGFILENAME
 258 
 259   if [ -w $SESSIONLOGFILENAME ]; then
 260       dtstart_sessionlogfile="$SESSIONLOGFILENAME"
 261   else
 262       dtstart_sessionlogfile="/dev/null"
 263   fi
 264 
 265   rm -f $SESSIONLOGFILENAME
 266 
 267 # 394
 268 
 269 
 270 # 30
 271 
 272   #
 273   # Determine Xsession parent
 274   #
 275 
 276 
 277 
 278 
 279 
 280   pexec=$(LC_TIME=C /usr/bin/ps -p $PPID | awk 'NR==2 {print $4}')
 281 
 282   Log "Xsession started by $pexec"
 283 
 284 
 285 # ##########################################################################
 286 #
 287 # Append desktop font aliases to font path
 288 #
 289 # ##########################################################################
 290 
 291 # 66
 292 
 293   if [ "${pexec##*/}" != "dtlogin" ]; then
 294     #
 295     # If Xsession launched by dtlogin, it is assumed that the desktop
 296     # font path has already been added by Xsetup, so no need to add it here.
 297     #
 298 
 299 # 92
 300 
 301     #
 302     # Append desktop font paths. Note: these directories should be
 303     # accessable by the X server. The file precedence is:
 304     #
 305     #   /etc/dt/config/xfonts/C
 306     #   /usr/dt/config/xfonts/C
 307     #   /etc/dt/config/xfonts/$LANG
 308     #   /usr/dt/config/xfonts/$LANG
 309     #
 310 
 311     Log "setting font path..."
 312 
 313 
 314 
 315 
 316     if [ "$DTXSERVERLOCATION" != "remote" ]; then
 317 
 318       #
 319       # Since X server is local, optimize by checking local desktop
 320       # font directories and making one call to xset.
 321       #
 322 
 323       if [ -f /etc/dt/config/xfonts/C/fonts.dir ]; then
 324           fontpath=/etc/dt/config/xfonts/C
 325       fi
 326 
 327       if [ -f /usr/dt/config/xfonts/C/fonts.dir ]; then
 328         if [ -z "$fontpath" ]; then
 329           fontpath=/usr/dt/config/xfonts/C
 330         else
 331           fontpath=$fontpath,/usr/dt/config/xfonts/C
 332         fi
 333       fi
 334 
 335       if [ "$LANG" != "C" ]; then
 336         if [ -f /etc/dt/config/xfonts/$LANG/fonts.dir ]; then
 337           if [ -z "$fontpath" ]; then
 338             fontpath=/etc/dt/config/xfonts/$LANG
 339           else
 340             fontpath=$fontpath,/etc/dt/config/xfonts/$LANG
 341           fi
 342         fi
 343       fi
 344 
 345       if [ "$LANG" != "C" ]; then
 346         if [ -f /usr/dt/config/xfonts/$LANG/fonts.dir ]; then
 347           if [ -z "$fontpath" ]; then
 348             fontpath=/usr/dt/config/xfonts/$LANG
 349           else
 350             fontpath=$fontpath,/usr/dt/config/xfonts/$LANG
 351           fi
 352         fi
 353       fi
 354 
 355 
 356       if [ ! -z "$fontpath" ]; then
 357         $XDIR/xset fp+ $fontpath
 358       fi
 359 
 360       fontpath=/usr/lib/X11/fonts/iso_8859.15/75dpi
 361 
 362       if [ ! -z "$fontpath" ]; then
 363         $XDIR/xset fp+ $fontpath
 364       fi
 365 
 366 
 367 
 368     else
 369       #
 370       # Since X server not local, we don't know if the desktop font
 371       # directories exist on the X server machine, so we have to
 372       # set them one at a time.
 373       #
 374 
 375       $XDIR/xset fp+ /etc/dt/config/xfonts/C 1>/dev/null
 376 
 377       $XDIR/xset fp+ /usr/dt/config/xfonts/C 1>/dev/null
 378 
 379       if [ "$LANG" != "C" ]; then
 380         $XDIR/xset fp+ /etc/dt/config/xfonts/$LANG 1>/dev/null
 381       fi
 382 
 383       if [ "$LANG" != "C" ]; then
 384         $XDIR/xset fp+ /usr/dt/config/xfonts/$LANG 1>/dev/null
 385       fi
 386 
 387       fontpath=/usr/lib/X11/fonts/iso_8859.15/75dpi
 388 
 389       if [ ! -z "$fontpath" ]; then
 390         $XDIR/xset fp+ $fontpath
 391       fi
 392 
 393     fi
 394 
 395   fi
 396 
 397 # 486
 398 
 399 
 400 # 407
 401 
 402 # ##########################################################################
 403 #
 404 #   Source user's desktop profile
 405 #
 406 #   This section determines if the user has a desktop profile in their
 407 #   home directory. If not, the desktop default profile is copied to
 408 #   the home directory. The desktop profile is then sourced. The purpose
 409 #   is to incorporate any per-user/per-session environment customizations
 410 #   and thereby propagate them to applications and desktop components.
 411 #
 412 # ##########################################################################
 413 
 414 
 415   DTSYSPROFILE=sys.dtprofile
 416   DTPROFILE=.dtprofile
 417 
 418 
 419 
 420 
 421   if [ ! -f $HOME/$DTPROFILE ]; then
 422 
 423     for i in $DT_CONFIG_PATH
 424     do
 425       if [ -f $i/$DTSYSPROFILE ]; then
 426         /usr/bin/awk '
 427           BEGIN {printit=1}
 428           /SYSPROFILE COMMENT START/ {printit=0; next}
 429           /SYSPROFILE COMMENT END/ {printit=1; next}
 430           printit==1 {print}' <$i/$DTSYSPROFILE >$HOME/$DTPROFILE
 431           /usr/bin/chmod 755 $HOME/$DTPROFILE
 432         break
 433       fi
 434     done
 435   fi
 436 
 437   #
 438   # source the .dtprofile.
 439   #
 440   if [ -f $HOME/$DTPROFILE ]; then
 441     Log "sourcing $HOME/$DTPROFILE..."
 442     . $HOME/$DTPROFILE
 443   fi
 444 
 445 
 446 # ##########################################################################
 447 #
 448 #  External Xsession processing section
 449 #
 450 #  This section searches the Xsession.d subdirectory and sources
 451 #  the files contained therein.  The purpose is to set up any
 452 #  per-user/per-session environment customizations and thereby propagate
 453 #  them to applications and desktop components.
 454 #
 455 # ##########################################################################
 456 
 457   DT_XSESSION_DIR=Xsession.d
 458 
 459   #
 460   # Run custom Xsession scripts for this session.
 461   # If multiple scripts have the same name, run only the first one found.
 462   #
 463   for SCRIPT in $(
 464     (
 465       for i in $DT_CONFIG_PATH; do
 466         if [[ -d $i/$DT_XSESSION_DIR ]]; then
 467           ls $i/$DT_XSESSION_DIR
 468         fi
 469       done
 470     ) | sort -u
 471   ); do
 472     for i in $DT_CONFIG_PATH
 473     do
 474       if [ -x $i/$DT_XSESSION_DIR/$SCRIPT -a \
 475           \( ! -d $i/$DT_XSESSION_DIR/$SCRIPT \) ]; then
 476              Log "sourcing $i/$DT_XSESSION_DIR/$SCRIPT..."
 477              . $i/$DT_XSESSION_DIR/$SCRIPT
 478              break
 479         fi
 480     done
 481   done
 482 
 483 
 484 
 485 # ##########################################################################
 486 #
 487 #   Startup section.
 488 #
 489 #   Note: The ksh syntax ${parameter%% *} is used when appropriate to
 490 #           remove any command line options that may have been included
 491 #           in the definition of a DT executable below.
 492 #
 493 # ##########################################################################
 494 
 495 #
 496 # Return first command in array named by $1 that is executable
 497 #
 498 GetFirst()
 499 {
 500   let i=0
 501   while true; do
 502     eval "cmd=\${$1[$i]}"
 503     [ -z "$cmd" ] && break
 504     [ -x "${cmd%% *}" ] && echo "$cmd" && break
 505     Log "could not start $cmd"
 506     let i=$i+1
 507   done
 508 }
 509 
 510 #
 511 # Start first command in array named by $1 that is executable. If
 512 # $2 is 'eval', command result will be 'eval'ed.
 513 #
 514 StartFirst()
 515 {
 516   first=$(GetFirst $1)
 517   if [ ! -z "$first" ]; then
 518     Log "starting $first"
 519     if [ "$2" = "eval" ]; then
 520       eval `eval "PATH=$DT_BINPATH:$PATH $first"`
 521     else
 522       eval "PATH=$DT_BINPATH:$PATH $first"
 523     fi
 524   fi
 525 }
 526 
 527   #
 528   # Prepare for session startup
 529   #
 530 
 531 
 532 
 533   if [ "$DTSOURCEPROFILE" = "true" ]
 534 
 535   then
 536     case ${SHELL##*/} in
 537        sh | ksh | dtksh) shellprofile="$HOME/.profile";;
 538        bash) shellprofile="$HOME/.bash_profile";;
 539        csh | tcsh) shellprofile="$HOME/.login";;
 540        *) Log "non-standard shell $SHELL"
 541     esac
 542   fi
 543 
 544   if [ "$shellprofile" -a ! -f "$shellprofile" ]
 545   then
 546     Log "could not read $shellprofile"
 547     unset shellprofile
 548   fi
 549 
 550   if [ "$SESSIONTYPE" = "xdm" ]; then
 551     startup=$(GetFirst xdmstart_session)    # get xdm session client
 552     StartFirst xdmstart_hello               # start xdm hello client
 553   else
 554     startup=$(GetFirst dtstart_session)     # get desktop session client
 555     if [ -n "$INFO_PATH" ]; then
 556        StartFirst dtstart_hello_info           # start desktop hello client
 557     else
 558        StartFirst dtstart_hello                # start desktop hello client
 559     fi
 560     StartFirst dtstart_searchpath eval      # setup desktop search paths
 561 
 562     tooltalk=$(GetFirst dtstart_ttsession)  # get tooltalk client
 563     dtdbcache=$(GetFirst dtstart_dtdbcache) # get dtdbcache client
 564 
 565 # 575
 566 
 567     StartFirst dtstart_appgather            # setup session applications
 568   fi
 569 
 570   #
 571   # Start the session.
 572   #
 573 
 574   if [ $shellprofile ]; then
 575     Log "execing $startup using $shellprofile..."
 576 
 577     source_profile=". $shellprofile"
 578     source_login="source $shellprofile"
 579   else
 580     Log "execing $startup..."
 581 
 582     source_profile="echo 'not sourcing $HOME/.profile (see $HOME/.dtprofile)'"
 583     source_login="echo 'not sourcing $HOME/.login (see $HOME/.dtprofile)'"
 584   fi
 585 
 586   if [ -z "$dtdbcache" ]; then
 587         dtdbcache="echo could not start $dtstart_dtdbcache"
 588   fi
 589 
 590   export DT=true;
 591   case ${SHELL##*/} in
 592       sh | bash) $SHELL -c "$source_profile; \
 593                            unset DT; \
 594 ############################################################################
 595 # Exiting from the script to return back to dtgreet screen in case dtdbcache
 596 # fails to initilaize Action database
 597 # ##########################################################################
 598                            $dtdbcache && \
 599                            (PATH=/usr/dt/bin:\$PATH $tooltalk;   \
 600                            $startup > $dtstart_sessionlogfile 2>&1)" ;;
 601 
 602       ksh | dtksh) $SHELL -c "$source_profile; \
 603                            unset DT; \
 604                            $dtdbcache && \
 605                            (PATH=/usr/dt/bin:\$PATH $tooltalk;\
 606                            $startup >| $dtstart_sessionlogfile 2>&1)" ;;
 607 
 608       csh | tcsh) $SHELL -c "unsetenv _ PWD;       \
 609                            $source_login; \
 610                            unsetenv DT;          \
 611                            $dtdbcache && \
 612                            ( (set path = ( $DT_BINPATH \$path ); $tooltalk ); \
 613                            $startup  >&! $dtstart_sessionlogfile)" ;;
 614 
 615       *) unset DT
 616          $dtdbcache || exit
 617          StartFirst dtstart_ttsession
 618          $startup >| $dtstart_sessionlogfile 2>&1 ;;
 619   esac
 620 
 621 $dtdbcacherm            # remove the actions/datatypes cachefile
 622 
 623 # ####################         eof      #################################
 624 

Are you frightened yet? You should be. The only thing saving this script from being a massive security hole is the fact that it's run without any special privileges.

The huge comment blocks are typical, and are not the worst I've seen. The indenting is not bad, though I dislike how the not-inside-function stuff is still indented two spaces. I don't understand what the # 112 (etc.) comments are supposed to mean, either. Perhaps they correspond to line numbers in some other file, from which this script is produced automatically? I don't know.

The first problem we see is a lack of Quotes.

  LOGDIR=$HOME/.dt
  LOGFILENAME=$LOGDIR/startlog
  [ -f $LOGFILENAME ] && mv -f $LOGFILENAME $LOGFILENAME.old

What happens here in the unlikely event that HOME contains spaces? Nothing good, to be sure. And it would be so easy to fix that.

They also parse the output of ls, here:

  for SCRIPT in $(
    (
      for i in $DT_CONFIG_PATH; do
        if [[ -d $i/$DT_XSESSION_DIR ]]; then
          ls $i/$DT_XSESSION_DIR
        fi
      done
    ) | sort -u
  ); do
    for i in $DT_CONFIG_PATH
    do
      ...

Yikes! What a disaster. Looks like they're taking a list of directory names, using ls to enumerate the contents of each one, shoving it all through sort -u, then WordSplitting the result of that and hoping it's still filenames. Well, sure, if you control all the files on the system (or at least in the directories pointed to by $DT_CONFIG_PATH) then you might be able to assume none of them contain spaces. But still, was this really the best approach they could think of?

How about this instead?

  for dir in "${DT_CONFIG_PATH[@]}"; do
    for SCRIPT in "$dir"/*; do
      [[ -x $SCRIPT ]] || continue
      ... run it ...
    done
  done

Granted, that doesn't eliminate duplicates (the way their sort -u does). Though I still think their requirement

  # If multiple scripts have the same name, run only the first one found.

is pretty daft. Is there a simple way to remove duplicates without having associative arrays available (and this system has ksh88, not ksh93, so they aren't)? Well, not exactly. Let's take one step back and see what they're really doing:

  DT_INSTALL_CONFIG=/usr/dt/config
  DT_CONFIG=/etc/dt/config
  DT_CONFIG_PATH="$DT_CONFIG $DT_INSTALL_CONFIG"

  for SCRIPT in $(
    (
      for i in $DT_CONFIG_PATH; do
       ...
    ) | sort -u
  ); do
    for i in $DT_CONFIG_PATH
    do
      if [ -x $i/$DT_XSESSION_DIR/$SCRIPT -a \
          \( ! -d $i/$DT_XSESSION_DIR/$SCRIPT \) ]; then
             Log "sourcing $i/$DT_XSESSION_DIR/$SCRIPT..."
             . $i/$DT_XSESSION_DIR/$SCRIPT
             break
        fi
    done
  done

I've omitted the distracting "parsing ls" part here, and included the variables from hundreds of lines earlier so we can see what they're trying to accomplish. They've actually got two directories: the sysadmin's customized one (/etc/dt/config) and the factory default one (/usr/dt/config). The sysadmin is supposed to copy files from the latter to the former, and modify them. So, they want to run all the scripts from /etc/dt/config and all the scripts from /usr/dt/config that weren't present in /etc/dt/config.

Given that explanation, the task becomes so much clearer. Start by not merging the two lists. Keep them separate.

  for script in "$DT_CONFIG"/*; do
    ... run it ...
  done
  for script in "$DT_INSTALL_CONFIG"/*; do
    basename=${script##*/}
    [[ -x $DT_CONFIG/$basename ]] && continue
    ... run it ...
  done

There! Isn't that much cleaner? Merging the two lists together simply muddied everything to the point that they probably couldn't see the original problem any more. They were stuck with this strange new problem to solve (removing duplicates from a merged list), but it was completely unnecessary.

But the part of this script that truly terrified me was this:

# Start first command in array named by $1 that is executable. If
# $2 is 'eval', command result will be 'eval'ed.
#
StartFirst()
{
  first=$(GetFirst $1)
  if [ ! -z "$first" ]; then
    Log "starting $first"
    if [ "$2" = "eval" ]; then
      eval `eval "PATH=$DT_BINPATH:$PATH $first"`
    else
      eval "PATH=$DT_BINPATH:$PATH $first"
    fi
  fi
}

Not just one, but two eval commands at the same time! Holy shit! What on earth prompted them to write that?

Let's see... this function is used from here:

  if [ "$SESSIONTYPE" = "xdm" ]; then
    startup=$(GetFirst xdmstart_session)    # get xdm session client
    StartFirst xdmstart_hello               # start xdm hello client

And we clearly need to see the GetFirst function too:

# Return first command in array named by $1 that is executable
#
GetFirst()
{
  let i=0
  while true; do
    eval "cmd=\${$1[$i]}"
    [ -z "$cmd" ] && break
    [ -x "${cmd%% *}" ] && echo "$cmd" && break
    Log "could not start $cmd"
    let i=$i+1
  done
}

(More eval voodoo!)

And here's what they're using for input to all this:

  xdmstart_session[0]="$HOME/.xsession"
  xdmstart_session[1]="/usr/lib/X11/xdm/sys.xsession"
  xdmstart_session[2]="$XDIR/xterm -geometry 80x24+10+10 -ls"
  xdmstart_hello="$XDIR/xsetroot -default &"

So... clearly their code design is driven by this data structure. They've got a bunch of commands inside variables. Then, later, they iterate through the array of commands, try to see which ones are executable, and then execute the first one that qualifies.

Is this complexity really needed? They don't use this xdmstart_session array anywhere else. It's never modified in any other part of this program. In theory, it could be modified by the user's $HOME/$DTPROFILE which is dotted in before this array is used; or by one of the scripts that are dotted in by the whole $DT_CONFIG section we discussed a moment ago. Whether that's something they wanted to allow, I can't say with certainty. I'm fairly sure they didn't intend for the end user to be overriding that array in their ~/.dtprofile file, but it's not stated either way.

If they didn't need that sort of complexity, then they could eliminate that array altogether. Just do this:

  if [ "$SESSIONTYPE" = "xdm" ]; then
    if [[ -x $HOME/.xsession ]]; then
      PATH="$DT_BINPATH:$PATH" "$HOME"/.xsession
    elif [[ -x /usr/lib/X11/xdm/sys.xsession ]]; then
      PATH="$DT_BINPATH:$PATH" /usr/lib/X11/xdm/sys.xsession
    elif [[ -x $XDIR/xterm ]]; then
      PATH="$DT_BINPATH:$PATH" "$XDIR"/xterm -geometry 80x24+10+10 -ls
    fi

Of course, they aren't actually running the command immediately. They're storing the command in yet another variable, then doing a whole bunch of manipulation, and finally handing it to a $SHELL -c to evaluate. But the same principle could be used there.

To be fair, however, the GetFirst/StartFirst pair is used on another array that's built dynamically:

  if [ "$SESSIONTYPE" = "altDt" ]; then
      dtstart_session[0]="$SDT_ALT_SESSION"
      ...
  else
      ...
      dtstart_session[0]="$DT_BINPATH/dtsession $DTSESSION_ARGS"
  fi
  dtstart_session[1]="$HOME/.xsession"
  dtstart_session[2]="$HOME/.x11start"
  dtstart_session[3]="$XDIR/xterm -geometry 80x24+10+10"

  ...
  else
    startup=$(GetFirst dtstart_session)     # get desktop session client

That was probably the inspiration for the whole "let's build an array of commands, then search through it for one that's executable later on, and then eval it" approach. It's an ugly solution to an ugly problem.

It's even uglier when we look at this bit:

      DTSESSION_ARGS=""
      if [ $# -ge 2 ]; then
          if [ "$1" = "-session" ]; then
              DTSESSION_ARGS="$1 $2"
          fi
      fi
      dtstart_session[0]="$DT_BINPATH/dtsession $DTSESSION_ARGS"

They've smashed the positional parameters together into a string. If the user supplied an argument that contains spaces (or glob characters, or is an empty string), this breaks.

Normally the solution to that problem would be to use an array. And at first glance, you might think "Well, hell, just put the user's arguments into a second array, and run "$command" "${extra_args[@]}"". That would work if it weren't for the whole $SHELL -c layer that comes later. They've made everything so complex that you have to destroy it completely before you can rebuild it.

That leaves us staring open-mouthed at the StartFirst function and its double eval. This page has already gone on long enough, though. At some point you just have to back away... slowly... without making any sudden movements.

<- Page 1: /usr/bin/lpr


2012-07-01 04:11