/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.