PageRenderTime 66ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/src/tools/tet/contrib/kshdapi/dapi/tetdapi.ksh

https://bitbucket.org/illumos/illumos-stc
Korn Shell | 1377 lines | 817 code | 150 blank | 410 comment | 77 complexity | 2c2ef5aa83efc4a4d50529f41c9a8d16 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. #
  2. # SCCS: @(#)tetdapi.ksh 1.4 (04/11/26)
  3. #
  4. # UniSoft Ltd., London, England
  5. #
  6. # (C) Copyright 1996 X/Open Company Limited
  7. # (C) Copyright 1999 UniSoft Ltd
  8. #
  9. # All rights reserved. No part of this source code may be reproduced,
  10. # stored in a retrieval system, or transmitted, in any form or by any
  11. # means, electronic, mechanical, photocopying, recording or otherwise,
  12. # except as stated in the end-user licence agreement, without the prior
  13. # permission of the copyright owners.
  14. # A copy of the end-user licence agreement is contained in the file
  15. # Licence which accompanies this distribution.
  16. #
  17. # X/Open and the 'X' symbol are trademarks of X/Open Company Limited in
  18. # the UK and other countries.
  19. #
  20. # ************************************************************************
  21. # Portions of this file are derived from the file tetapi.ksh which
  22. # contains the following notice:
  23. #
  24. # Copyright 1990 Open Software Foundation (OSF)
  25. # Copyright 1990 Unix International (UI)
  26. # Copyright 1990 X/Open Company Limited (X/Open)
  27. # Copyright 1991 Hewlett-Packard Co. (HP)
  28. #
  29. # Permission to use, copy, modify, and distribute this software and its
  30. # documentation for any purpose and without fee is hereby granted, provided
  31. # that the above copyright notice appear in all copies and that both that
  32. # copyright notice and this permission notice appear in supporting
  33. # documentation, and that the name of HP, OSF, UI or X/Open not be used in
  34. # advertising or publicity pertaining to distribution of the software
  35. # without specific, written prior permission. HP, OSF, UI and X/Open make
  36. # no representations about the suitability of this software for any purpose.
  37. # It is provided "as is" without express or implied warranty.
  38. #
  39. # HP, OSF, UI and X/Open DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  40. # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  41. # EVENT SHALL HP, OSF, UI or X/Open BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  42. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  43. # USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  44. # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  45. # PERFORMANCE OF THIS SOFTWARE.
  46. #
  47. # ***********************************************************************
  48. #
  49. # SCCS: @(#)tetdapi.ksh 1.4 04/11/26 TETware release 3.8
  50. # NAME: Distributed Korn Shell API Support Routines
  51. # PRODUCT: TET (Test Environment Toolkit)
  52. # AUTHOR: Andrew Dingwall, UniSoft Ltd.
  53. # DATE CREATED: May 1999
  54. # Parts of this file
  55. # derived from (non-distributed) tetapi.ksh v1.4
  56. #
  57. # DESCRIPTION:
  58. # This file contains shell functions for use with the shell API.
  59. # It is sourced automatically by the shell TCM.
  60. # In addition it should be sourced by test purposes that are written as
  61. # separate shell scripts, by means of the shell . command.
  62. #
  63. # The following functions are provided:
  64. #
  65. # tet_api_init_child
  66. # tet_delete
  67. # tet_exit
  68. # tet_fork
  69. # tet_getsysbyid
  70. # tet_infoline
  71. # tet_logoff
  72. # tet_minfoline
  73. # tet_reason
  74. # tet_remgetlist
  75. # tet_remgetsys
  76. # tet_remsync
  77. # tet_result
  78. # tet_setblock
  79. # tet_setcontext
  80. #
  81. # The following variables are provided:
  82. #
  83. # tet_child
  84. # tet_reply_code
  85. # tet_syncerr
  86. #
  87. # MODIFICATIONS:
  88. #
  89. #
  90. # ***********************************************************************
  91. #
  92. # API global variables
  93. #
  94. # tet_syncerr - the name of the sync error handler function
  95. # that is called by the API thus:
  96. #
  97. # $tet_syncerr spno "sysid sync-state" ...
  98. #
  99. typeset tet_syncerr=tet_syncreport
  100. # tet_reply_code - the server reply code from the helper process
  101. typeset tet_reply_code=INTERN
  102. # tet_child - pid of subshell, set by tet_fork
  103. typeset -i tet_child=0
  104. #
  105. # "private" variables for internal use by the API
  106. # these are not published interfaces and may go away one day
  107. #
  108. typeset TET_ALARM_FLAG=no
  109. typeset -i TET_ALARM_PID=0
  110. typeset TET_API_VERSION=
  111. typeset -i TET_CONTEXT=0
  112. typeset -i TET_KSHAPID_PID=0
  113. typeset TET_KSHAPID_REQUEST=
  114. typeset TET_KSHAPID_USABLE=no
  115. typeset TET_MERROR_INPROGRESS=no
  116. typeset -r TET_PNAME=$0
  117. typeset -i TET_REPLY_ARGC=0
  118. typeset TET_REPLY_ARGV
  119. typeset TET_REPLY_DATA_LINE
  120. typeset TET_TMPFILES=
  121. typeset -i TET_USYNC_NSYS=0
  122. typeset TET_USYNC_SYNCSTAT
  123. #
  124. # publicly available shell API functions
  125. #
  126. # tet_setcontext - set current context and reset block and sequence
  127. # usage: tet_setcontext
  128. # Note that when tet_setcontext is called in a subshell started using
  129. # "( ... )" we cannot use $$ because it has the same value as in the parent.
  130. tet_setcontext()
  131. {
  132. if test $$ -ne $TET_CONTEXT
  133. then
  134. TET_CONTEXT=$$
  135. else
  136. # obtain a new, unused PID without generating a zombie process.
  137. TET_CONTEXT=`(:)& echo $!`
  138. fi
  139. tet_kshapid_context $TET_CONTEXT
  140. }
  141. # tet_setblock - increment the current block ID, reset the sequence number to 1
  142. # usage: tet_setblock
  143. tet_setblock()
  144. {
  145. tet_kshapid_setblock
  146. }
  147. # tet_infoline - print an information line to the execution results file
  148. # usage: tet_infoline args ...
  149. tet_infoline()
  150. {
  151. tet_kshapid_minfoline "$*"
  152. }
  153. # tet_minfoline - print a group of information lines to the execution
  154. # results file as an atomic operation
  155. # usage: tet_minfoline lines ...
  156. tet_minfoline()
  157. {
  158. tet_kshapid_minfoline "$@"
  159. }
  160. # tet_result - record a test result
  161. # usage: tet_result result_name
  162. # (note that a result name is expected, not a result code number)
  163. tet_result()
  164. {
  165. tet_kshapid_result ${1:?}
  166. }
  167. # tet_delete - mark a test purpose as deleted, or undelete it
  168. # usage: tet_delete test_name [reason ...]
  169. tet_delete()
  170. {
  171. typeset TET_ARG1=${1:?}
  172. shift
  173. typeset TET_ARG2N="$*"
  174. if test -z "$TET_ARG2N"
  175. then
  176. tet_undelete $TET_ARG1
  177. return
  178. fi
  179. if tet_reason $TET_ARG1 > ${TET_DEVNULL:?}
  180. then
  181. tet_undelete $TET_ARG1
  182. fi
  183. echo "$TET_ARG1 $TET_ARG2N" >> ${TET_DELETES:?}
  184. }
  185. # tet_reason - print the reason why a test purpose has been deleted
  186. # return 0 if the test purpose has been deleted, 1 otherwise
  187. # usage: tet_reason test_name
  188. tet_reason()
  189. {
  190. typeset -i TET_return=0
  191. typeset TET_A=
  192. typeset TET_B=
  193. : ${1:?}
  194. let TET_return=1
  195. while read TET_A TET_B
  196. do
  197. if test X"$TET_A" = X"$1"
  198. then
  199. echo "$TET_B"
  200. let TET_return=0
  201. break
  202. fi
  203. done < ${TET_DELETES:?}
  204. return $TET_return
  205. }
  206. # tet_fork - start a subshell
  207. # usage: tet_fork childfunc [parentfunc [waittime [validresults]]]
  208. #
  209. # Invokes childfunc in a subshell.
  210. # If parentfunc is non-empty, invokes parentfunc at the same time.
  211. # If waittime is +ve:
  212. # tet_fork waits waittime seconds after parentfunc returns,
  213. # then kills the childfunc if it is still running.
  214. # If waittime is 0:
  215. # tet_fork waits indefinitely for childfunc to return.
  216. # If waittime is -ve:
  217. # tet_fork does not wait for childfunc to return or report on the
  218. # subshell's exit status; instead, parentfunc is expected to wait
  219. # for childfunc to return, using the PID stored in $tet_child.
  220. #
  221. # If waittime is 0 or +ve:
  222. # tet_fork examines the subshell's exit status.
  223. # If the subshell is terminated by a signal:
  224. # tet_fork prints a diagnostic to the journal and reports
  225. # a result of UNRESOLVED.
  226. # Otherwise:
  227. # Bits set in validresults are cleared in the exit status.
  228. # If the result of this operation is non-zero, tet_fork
  229. # prints a diagnostic to the journal and reports a result
  230. # of UNRESOLVED.
  231. # When tet_fork reports UNRESOLVED it returns 255,
  232. # otherwise tet_fork returns the subshell's exit status.
  233. # If waittime is -ve:
  234. # tet_fork always returns 0.
  235. #
  236. # A childfunc should return or exit with a status in the range 0 -> 125.
  237. tet_fork()
  238. {
  239. # local variables
  240. typeset tet_l40_childfunc="${1:?}"
  241. typeset tet_l40_parentfunc="$2"
  242. typeset -i tet_l40_waittime=${3:-0}
  243. typeset -i tet_l40_validresults=${4:-0}
  244. typeset -i tet_l40_rc=0
  245. typeset -i tet_l40_savechild=$tet_child
  246. typeset -i tet_l40_sig=0
  247. typeset -i tet_l40_testnum=${TET_TESTNUM:?}
  248. typeset tet_l40_unresolved=yes
  249. # NOTE: restore tet_child before all returns
  250. # fork the subshell and invoke the childfunc
  251. (
  252. if test $tet_l40_waittime -ge 0
  253. then
  254. trap ${TET_DEFAULT_SIGS:?}
  255. fi
  256. TET_TMPFILES=
  257. tet_api_init_child
  258. tet_kshapid_thistest $tet_l40_testnum
  259. $tet_l40_childfunc
  260. tet_exit $?
  261. ) &
  262. tet_child=$!
  263. # invoke the parentfunc if so required
  264. if test -n "$tet_l40_parentfunc"
  265. then
  266. tet_setblock
  267. $tet_l40_parentfunc
  268. fi
  269. tet_setblock
  270. # -ve waittime means no wait; the parent does the wait and so
  271. # the child must be killed if it is still around
  272. if test $tet_l40_waittime -lt 0
  273. then
  274. tet_killw $tet_child 10
  275. wait $tet_child
  276. tet_child=$tet_l40_savechild
  277. return 0
  278. fi
  279. # wait for the child to complete (possibly with timeout)
  280. if test $tet_l40_waittime -gt 0
  281. then
  282. TET_ALARM_FLAG=no
  283. trap 'TET_ALARM_FLAG=yes' USR2
  284. tet_set_alarm $tet_l40_waittime
  285. fi
  286. wait $tet_child
  287. tet_l40_rc=$?
  288. if test $tet_l40_waittime -gt 0
  289. then
  290. tet_clear_alarm
  291. fi
  292. # report on the child function's exit status
  293. if test $TET_ALARM_FLAG = yes
  294. then
  295. tet_infoline "tet_fork: child function timed out"
  296. tet_killw $tet_child 10
  297. else
  298. if test $tet_l40_rc -gt 256
  299. then
  300. # process killed by a signal in ksh93
  301. tet_l40_sig=$((tet_l40_rc - 256))
  302. elif test $tet_l40_rc -gt 128
  303. then
  304. # process killed by a signal in other shells
  305. tet_l40_sig=$((tet_l40_rc - 128))
  306. fi
  307. if test $tet_l40_sig -gt 0
  308. then
  309. tet_infoline "tet_fork: child function terminated by signal $tet_l40_sig"
  310. elif test $tet_l40_rc -eq 127
  311. then
  312. tet_infoline "tet_fork: wait for child function failed (wait returned 127)"
  313. elif test $((tet_l40_rc & ~tet_l40_validresults)) -eq 0
  314. then
  315. # ok
  316. tet_l40_unresolved=no
  317. else
  318. tet_infoline "tet_fork: child function returned unexpected exit status $tet_l40_rc"
  319. fi
  320. fi
  321. # report UNRESOLVED if so required
  322. if test $tet_l40_unresolved = yes
  323. then
  324. tet_result UNRESOLVED
  325. tet_l40_rc=255
  326. fi
  327. # finally, restore tet_child and return
  328. tet_child=$tet_l40_savechild
  329. return $tet_l40_rc
  330. }
  331. # tet_remsync - perform a user sync
  332. # usage: tet_remsync spno vote timeout "sysids" [smvar]
  333. #
  334. # sysids is a single argument which contains a (space separated) list of
  335. # system IDs
  336. #
  337. # If specified, smvar is the basename of variables which describes sync
  338. # message data.
  339. # It is used both to pass information into this function and to receive
  340. # return values, and is analogous to struct tet_synmsg in the C API.
  341. # smvar_LINE[] is an array which contains lines of sync message data
  342. # (starting at smvar_LINE[0]).
  343. # smvar_NLINES specifies the number of lines in smvar_LINE[].
  344. # smvar_SYSID is used to return the system ID of the sending system.
  345. # smvar_FLAGS contains a (space separated) list of sync message flags:
  346. # SNDMSG, RCVMSG, DUP, TRUNC
  347. #
  348. # Return 0 if successful or non-zero on error.
  349. # This function sets tet_reply_code before returning.
  350. # Possible errors include: SYNCERR TIMEDOUT DONE DUPS
  351. tet_remsync()
  352. {
  353. # local variables
  354. typeset -i tet_l10_spno=${1:?}
  355. typeset tet_l10_vote="${2:?}"
  356. typeset -i tet_l10_timeout=${3:?}
  357. typeset tet_l10_sysids="${4:?}"
  358. typeset tet_l10_smvar=$5
  359. # handle user-generated INVAL errors here
  360. if test $tet_l10_spno -lt 0 -o \
  361. \( "$tet_l10_vote" != YES -a "$tet_l10_vote" != NO \)
  362. then
  363. tet_reply_code=INVAL
  364. return 1
  365. fi
  366. # call tet_remsync() in the helper process
  367. if tet_kshapid_usync $tet_l10_spno $tet_l10_vote $tet_l10_timeout \
  368. "$tet_l10_sysids" $tet_l10_smvar
  369. then
  370. return 0
  371. fi
  372. # call the sync error handler if there is one
  373. if test -n "$tet_syncerr"
  374. then
  375. case $tet_reply_code in
  376. SYNCERR|TIMEDOUT)
  377. $tet_syncerr $tet_l10_spno "${TET_USYNC_SYNCSTAT[@]}"
  378. ;;
  379. *)
  380. $tet_syncerr $tet_l10_spno
  381. ;;
  382. esac
  383. fi
  384. return 1
  385. }
  386. # tet_syncreport - the default user sync error handler
  387. # usage: tet_syncreport spno "sysid sync-state" ...
  388. #
  389. # The API only calls this function when tet_reply_code is SYNCERR or TIMEDOUT;
  390. # but other reply codes are catered for in case the user calls this function
  391. # under other circumstances.
  392. tet_syncreport()
  393. {
  394. # local variables
  395. typeset -i tet_l22_n=0
  396. typeset -i tet_l22_sysid=0
  397. typeset tet_l22_save_reply_code=
  398. typeset tet_l22_msg
  399. typeset tet_l22_systate=
  400. typeset tet_l22_text=
  401. tet_l22_text="sync operation failed, syncptno = ${1:?}"
  402. shift
  403. unset tet_l22_msg
  404. case "$tet_reply_code" in
  405. SYNCERR)
  406. if test $# -gt 1
  407. then
  408. tet_l22_msg[0]="$tet_l22_text, one or more of the other systems did not sync, voted NO or timed out"
  409. else
  410. tet_l22_msg[0]="$tet_l22_text, other system did not sync, voted NO or timed out"
  411. fi
  412. ;;
  413. TIMEDOUT)
  414. tet_l22_msg[0]="$tet_l22_text, request timed out"
  415. ;;
  416. DUPS)
  417. tet_l22_msg[0]="$tet_l22_text, duplicate sysids in system list"
  418. ;;
  419. DONE)
  420. tet_l22_msg[0]="$tet_l22_text, event already happened"
  421. ;;
  422. *)
  423. tet_l22_msg[0]="$tet_l22_text, unexpected server reply code $tet_reply_code"
  424. ;;
  425. esac
  426. # generate the per-system diagnostics
  427. case "$tet_reply_code" in
  428. SYNCERR|TIMEDOUT)
  429. while test $# -gt 0
  430. do
  431. eval "`(
  432. set -- ${1:?}
  433. echo tet_l22_sysid=\$1
  434. echo tet_l22_systate=\${2:-unknown}
  435. )`"
  436. tet_l22_msg[$((tet_l22_n += 1))]="system = $tet_l22_sysid, state = $tet_l22_systate"
  437. shift
  438. done
  439. ;;
  440. esac
  441. # finally, print all the diagnostics at once
  442. tet_l22_save_reply_code=$tet_reply_code
  443. tet_merror "${tet_l22_msg[@]}"
  444. tet_reply_code=$tet_l22_save_reply_code
  445. }
  446. # tet_remgetlist - print the list of other systems participating in a
  447. # distributed test
  448. # usage: tet_remgetlist
  449. tet_remgetlist()
  450. {
  451. # local variables
  452. typeset tet_l11_snames=
  453. typeset tet_l11_sname=
  454. # generate the list of other systems
  455. for tet_l11_sname in ${TET_SNAMES:?}
  456. do
  457. if test $tet_l11_sname -ne ${TET_MYSYSID:?}
  458. then
  459. tet_l11_snames="$tet_l11_snames${tet_l11_snames:+ }$tet_l11_sname"
  460. fi
  461. done
  462. print $tet_l11_snames
  463. }
  464. # tet_remgetsys - print the system ID of the calling process
  465. # usage: tet_remgetsys
  466. tet_remgetsys()
  467. {
  468. # for some bizarre reason, when an integer variable is exported in
  469. # ksh93, its value is changed into the form base#number
  470. # (for example: "typeset -xi foo=2; echo $foo" prints 10#2)
  471. # so we must assign TET_MYSYSID to a non-exported variable
  472. # before printing it
  473. # local variables
  474. typeset -i tet_l23_mysysid=${TET_MYSYSID:?}
  475. print $tet_l23_mysysid
  476. }
  477. # tet_getsysbyid - systems file lookup function
  478. # usage: tet_getsysbyid sysid sysent
  479. #
  480. # sysent is the basename of some variables in the calling function which
  481. # are to receive the systems entry.
  482. # These variables are analogous to struct tet_sysent in the C API.
  483. # When the call is successful, the sysid is returned in sysent_SYSID and
  484. # the host name is returned in sysent_NAME.
  485. #
  486. # return 0 if successful or non-zero on error
  487. #
  488. tet_getsysbyid()
  489. {
  490. typeset -i tet_l25_sysid=${1:?}
  491. typeset tet_l25_sysent=${2:?}
  492. if test $tet_l25_sysid -lt 0
  493. then
  494. tet_reply_code=INVAL
  495. return 1
  496. fi
  497. tet_kshapid_request tet_getsysbyid $tet_l25_sysid
  498. tet_kshapid_check_reply tet_getsysbyid 2 ANY
  499. tet_kshapid_release
  500. case "$tet_reply_code" in
  501. OK)
  502. eval ${tet_l25_sysent}_SYSID='${TET_REPLY_ARGV[0]}'
  503. eval ${tet_l25_sysent}_NAME='${TET_REPLY_ARGV[1]}'
  504. return 0
  505. ;;
  506. *)
  507. return 1
  508. ;;
  509. esac
  510. }
  511. # tet_api_init_child - initialise the API and start the helper process.
  512. # This function must be called exactly ONCE in each sub-program.
  513. # usage: tet_api_init_child
  514. tet_api_init_child()
  515. {
  516. TET_KSHAPID_PID=0
  517. tet_api_init -c
  518. }
  519. # tet_logoff - log off the helper process
  520. # usage: tet_logoff
  521. tet_logoff()
  522. {
  523. # shut down the helper process
  524. if test "$TET_KSHAPID_USABLE" = yes
  525. then
  526. tet_kshapid_shutdown
  527. else
  528. tet_kshapid_close
  529. fi
  530. }
  531. # tet_exit - exit from a test case
  532. # usage: tet_exit [exit-status]
  533. tet_exit()
  534. {
  535. # local variables
  536. typeset tet_l12_rc=${1:-$?}
  537. # log off servers
  538. tet_logoff
  539. # remove temporary files
  540. # NOTE: the correct operation of this code in a subprogram relies
  541. # on TET_TMPFILES being initialised to an empty string in this file
  542. if test -n "$TET_TMPFILES"
  543. then
  544. rm -f $TET_TMPFILES
  545. TET_TMPFILES=
  546. fi
  547. trap 0
  548. exit $tet_l12_rc
  549. }
  550. # ******************************************************************
  551. #
  552. # "private" functions for internal use by the shell API
  553. # these are not published interfaces and may go away one day
  554. #
  555. # tet_undelete - undelete a test purpose
  556. tet_undelete()
  557. {
  558. echo "g/^${1:?} /d
  559. w
  560. q" | ed - ${TET_DELETES:?}
  561. }
  562. # tet_api_init - initialise the API
  563. # usage: tet_api_init [kshapid-args ...]
  564. tet_api_init()
  565. {
  566. # close an existing co-process descriptor
  567. # which might have been inherited from a parent shell
  568. tet_kshapid_close
  569. # start a new instance of the helper process
  570. tet_kshapid_startup ${1:+"$@"}
  571. # Set the context.
  572. # In a top-level TCM or a sub-program the initial value of TET_CONTEXT
  573. # is 0, so tet_setcontext sets the context to $$.
  574. # In a subshell the value of TET_CONTEXT is inherited from the
  575. # parent shell, so tet_setcontext sets the context to a unique value;
  576. tet_setcontext
  577. }
  578. # tet_killw - send a SIGTERM to a process;
  579. # if the process doesn't exit after the specified time, send a SIGKILL
  580. # usage: tet_killw pid timeout
  581. tet_killw()
  582. {
  583. typeset -i tet_l41_pid=${1:?}
  584. typeset -i tet_l41_timeout=${2:?}
  585. typeset tet_l41_sig=
  586. for tet_l41_sig in TERM KILL
  587. do
  588. if kill -$tet_l41_sig $tet_l41_pid 2> ${TET_DEVNULL:?}
  589. then
  590. : ok
  591. else
  592. break
  593. fi
  594. TET_ALARM_FLAG=no
  595. trap 'TET_ALARM_FLAG=yes' USR2
  596. tet_set_alarm $tet_l41_timeout
  597. wait $tet_l41_pid
  598. tet_clear_alarm
  599. if test $TET_ALARM_FLAG = no
  600. then
  601. break
  602. fi
  603. done
  604. }
  605. # tet_set_alarm - schedule an alarm call
  606. # usage: tet_set_alarm timeout
  607. #
  608. # We use SIGUSR2 for the alarm signal because ksh seems to get confused
  609. # when we use SIGALRM.
  610. tet_set_alarm()
  611. {
  612. typeset -i tet_l42_timeout=${1:?}
  613. typeset -i tet_l42_mypid=0
  614. tet_l42_mypid=`${TET_ROOT:?}/bin/tet_getpid`
  615. if test $? -ne 0 -o $((tet_l42_mypid)) -le 0
  616. then
  617. tet_fatal "tet_getpid failed in tet_set_alarm !!"
  618. fi
  619. if test $tet_l42_timeout -le 0
  620. then
  621. return
  622. fi
  623. (
  624. trap "exit 0" TERM
  625. trap 0
  626. sleep $tet_l42_timeout
  627. kill -USR2 $tet_l42_mypid
  628. ) &
  629. TET_ALARM_PID=$!
  630. }
  631. # tet_clear_alarm - cancel a previously scheduled alarm call
  632. # usage: tet_clear_alarm
  633. tet_clear_alarm()
  634. {
  635. if test $TET_ALARM_PID -gt 0
  636. then
  637. kill $TET_ALARM_PID 2> ${TET_DEVNULL:?}
  638. TET_ALARM_PID=0
  639. fi
  640. }
  641. # ******************************************************************
  642. #
  643. # error reporting functions
  644. #
  645. # When one of these functions is called from below one of the helper process
  646. # interface functions it is necessary to interact with the request/release
  647. # mechanism in order to guard against recursive calls.
  648. # To do this, either set TET_KSHAPID_USABLE=no or call tet_kshapid_release
  649. # before the call.
  650. # tet_error - print an error message to stderr and to the journal
  651. # usage: tet_error text ...
  652. tet_error()
  653. {
  654. tet_merror "$*"
  655. }
  656. # tet_merror - print multiple error message lines to stderr and to the journal
  657. # the message lines are printed to the journal as an atomic operation
  658. # usage: tet_merror "line" ...
  659. tet_merror()
  660. {
  661. # local variables
  662. typeset -i tet_l13_n=0
  663. typeset -i tet_l13_sysid=${TET_MYSYSID:?}
  664. # print each line in turn to stderr
  665. while test $((tet_l13_n += 1)) -le $#
  666. do
  667. eval print -u2 -R \"${TET_PNAME:-TCM/API} system $tet_l13_sysid: \$$tet_l13_n\"
  668. done
  669. # punt the line(s) to the helper process if possible
  670. if test "$TET_KSHAPID_USABLE" = yes -a "$TET_MERROR_INPROGRESS" != yes
  671. then
  672. TET_MERROR_INPROGRESS=yes
  673. tet_kshapid_merror "$@"
  674. TET_MERROR_INPROGRESS=no
  675. fi
  676. }
  677. # tet_fatal - fatal error reporting function
  678. # usage: tet_fatal text ...
  679. tet_fatal()
  680. {
  681. tet_merror "$*"
  682. exit 1
  683. }
  684. # tet_mfatal - fatal error reporting function
  685. # usage: tet_mfatal "line" ...
  686. tet_mfatal()
  687. {
  688. tet_merror "$@"
  689. exit 1
  690. }
  691. # ******************************************************************
  692. #
  693. # ksh API helper process interface functions
  694. #
  695. # tet_kshapid_startup - start the Distributed Korn Shell API helper process
  696. # usage: tet_kshapid_startup [kshapid-args ...]
  697. tet_kshapid_startup()
  698. {
  699. # local variables
  700. typeset tet_l14_kshapid=
  701. typeset -i tet_l14_context=0
  702. typeset -i tet_l14_pid=0
  703. case "${TET_OSNAME}" in
  704. Windows_NT|Windows_9[58]|DOS)
  705. tet_l14_kshapid=${TET_ROOT:?}/bin/tetkshapid.exe
  706. ;;
  707. *)
  708. tet_l14_kshapid=${TET_ROOT:?}/bin/tetkshapid
  709. ;;
  710. esac
  711. # ensure that the ksh API helper process is executable
  712. if test ! -x $tet_l14_kshapid
  713. then
  714. tet_fatal "$tet_l14_kshapid is not executable"
  715. fi
  716. # start up the ksh API helper process as a co-process
  717. $tet_l14_kshapid ${1:+"$@"} |&
  718. tet_l14_pid=$!
  719. # ensure that the helper process started successfully
  720. sleep 1
  721. if kill -0 $tet_l14_pid 2> ${TET_DEVNULL:?}
  722. then
  723. TET_KSHAPID_USABLE=yes
  724. TET_KSHAPID_REQUEST=
  725. TET_KSHAPID_PID=$tet_l14_pid
  726. else
  727. TET_KSHAPID_USABLE=no
  728. tet_fatal "startup error in ksh API helper process"
  729. fi
  730. return 0
  731. }
  732. # tet_kshapid_shutdown - shut down the helper process and close the connection
  733. # usage: tet_kshapid_shutdown
  734. tet_kshapid_shutdown()
  735. {
  736. tet_kshapid_request tet_shutdown
  737. tet_kshapid_check_reply tet_shutdown 0 ANY
  738. tet_kshapid_release
  739. tet_kshapid_close
  740. if test ${TET_KSHAPID_PID:?} -gt 0
  741. then
  742. wait $TET_KSHAPID_PID
  743. fi
  744. return 0
  745. }
  746. # tet_kshapid_close - close the connection to the helper process
  747. # usage: tet_kshapid_close
  748. #
  749. # NOTE this used fd 9 so that fd isn't available for use by test cases
  750. tet_kshapid_close()
  751. {
  752. TET_KSHAPID_USABLE=no
  753. # see if the coprocess descriptor is open;
  754. # if the helper process has already terminated and the shell has
  755. # noticed, the shell might have already closed the coprocess
  756. # descriptor for us
  757. if print -p -n "" 2> ${TET_DEVNULL:?}
  758. then
  759. exec 9>&p
  760. exec 9>&-
  761. exec 9<&p
  762. exec 9<&-
  763. fi
  764. }
  765. # tet_kshapid_context - set the context in the helper process
  766. # (also sets the block and sequence to 1)
  767. # usage: tet_kshapid_context context
  768. tet_kshapid_context()
  769. {
  770. tet_kshapid_request tet_context ${1:?}
  771. tet_kshapid_check_reply tet_context 0
  772. tet_kshapid_release
  773. return 0
  774. }
  775. # tet_kshapid_setblock - calls tet_setblock() in the helper process
  776. # usage: tet_kshapid_setblock
  777. tet_kshapid_setblock()
  778. {
  779. tet_kshapid_request tet_setblock
  780. tet_kshapid_check_reply tet_setblock 0
  781. tet_kshapid_release
  782. return 0
  783. }
  784. # tet_kshapid_thistest - send a tet_thistest request to the helper process
  785. # usage: tet_kshapid_thistest testnum
  786. tet_kshapid_thistest()
  787. {
  788. tet_kshapid_request tet_thistest ${1:?}
  789. tet_kshapid_check_reply tet_thistest 0
  790. tet_kshapid_release
  791. return 0
  792. }
  793. # tet_kshapid_minfoline - calls tet_minfoline() in the helper process
  794. # usage: tet_kshapid_minfoline lines ...
  795. tet_kshapid_minfoline()
  796. {
  797. if test $# -eq 0
  798. then
  799. return 0
  800. fi
  801. tet_kshapid_request tet_minfoline $#
  802. while test $# -gt 0
  803. do
  804. tet_kshapid_request_data "$1"
  805. shift
  806. done
  807. tet_kshapid_check_reply tet_minfoline 0
  808. tet_kshapid_release
  809. return 0
  810. }
  811. # tet_kshapid_result - calls tet_result() in the helper process
  812. # usage: tet_kshapid_result result-name
  813. # result-name should be the NAME of a valid result as defined in the
  814. # $TET_CODE file or in the internal table
  815. tet_kshapid_result()
  816. {
  817. : ${1:?}
  818. tet_kshapid_request tet_result 1
  819. tet_kshapid_request_data "$*"
  820. tet_kshapid_check_reply tet_result 0
  821. tet_kshapid_release
  822. return 0
  823. }
  824. # tet_kshapid_usync - send a user sync request to the helper process
  825. # usage: tet_kshapid_usync spno vote timeout "sysids" [smvar]
  826. #
  827. # The meanings of the arguments are as described in tet_remsync above.
  828. #
  829. # Return 0 if OK or non-zero on error.
  830. # On return the return code is in tet_reply_code.
  831. # When tet_reply_code is SYNCERR or TIMEDOUT,
  832. # the list of (sysid, sync-state) pairs is in TET_USYNC_SYNCSTAT[],
  833. # and the number of systems in the list is in TET_USYNC_NSYS.
  834. tet_kshapid_usync()
  835. {
  836. # local variables
  837. typeset -i tet_l16_n=0
  838. typeset -i tet_l16_ndlines=0
  839. typeset -i tet_l16_nlines=1
  840. typeset -i tet_l16_smsysid=-1
  841. typeset tet_l16_dline=
  842. typeset tet_l16_smdata=
  843. typeset tet_l16_smflags=
  844. # see if we want to send or receive sync message data
  845. if test $# -gt 4
  846. then
  847. tet_l16_smdata=${5:?}
  848. eval tet_l16_ndlines=\$${tet_l16_smdata}_NLINES
  849. eval tet_l16_smflags=\"\$${tet_l16_smdata}_FLAGS\"
  850. if test $tet_l16_ndlines -lt 0
  851. then
  852. tet_l16_ndlines=0
  853. fi
  854. : $((tet_l16_nlines += tet_l16_ndlines + 1))
  855. fi
  856. # send the request and the system ID list
  857. typeset tet_l16_sysnames="${4:?}"
  858. tet_kshapid_request tet_usync ${1:?} ${2:?} ${3:?} $tet_l16_nlines
  859. tet_kshapid_request_data $tet_l16_sysnames
  860. # send the sync message data information
  861. if test -n "$tet_l16_smdata"
  862. then
  863. tet_kshapid_request_data $tet_l16_smflags
  864. case "$tet_l16_smflags" in
  865. *SNDMSG*)
  866. tet_l16_n=-1
  867. while test $((tet_l16_n += 1)) -lt $tet_l16_ndlines
  868. do
  869. eval tet_l16_dline="\"\${${tet_l16_smdata}_LINE[$tet_l16_n]}\""
  870. tet_kshapid_request_data "$tet_l16_dline"
  871. done
  872. ;;
  873. esac
  874. fi
  875. # receive the reply
  876. tet_kshapid_check_reply tet_usync 3 ANY
  877. TET_USYNC_NSYS=${TET_REPLY_ARGV[0]}
  878. tet_l16_ndlines=${TET_REPLY_ARGV[1]}
  879. tet_l16_smsysid=${TET_REPLY_ARGV[2]}
  880. # receive the per-system information
  881. tet_kshapid_read_reply_data $TET_USYNC_NSYS
  882. unset TET_USYNC_SYNCSTAT
  883. case "$tet_reply_code" in
  884. SYNCERR|TIMEDOUT)
  885. tet_l16_n=-1
  886. while test $((tet_l16_n += 1)) -lt $TET_USYNC_NSYS
  887. do
  888. TET_USYNC_SYNCSTAT[$tet_l16_n]="${TET_REPLY_DATA_LINE[$tet_l16_n]}"
  889. done
  890. ;;
  891. esac
  892. # receive the sync message data if there is any
  893. if test -n "$tet_l16_smdata"
  894. then
  895. eval ${tet_l16_smdata}_NLINES=$tet_l16_ndlines
  896. eval ${tet_l16_smdata}_SYSID=$tet_l16_smsysid
  897. case "$tet_reply_code" in
  898. OK|SYNCERR)
  899. tet_kshapid_read_reply_data $((tet_l16_ndlines + 1))
  900. tet_l16_smflags="${TET_REPLY_DATA_LINE[0]}"
  901. eval ${tet_l16_smdata}_FLAGS="\"$tet_l16_smflags\""
  902. if test $tet_l16_smsysid -ge 0
  903. then
  904. case "$tet_l16_smflags" in
  905. *RCVMSG*)
  906. eval unset ${tet_l16_smdata}_LINE
  907. tet_l16_n=-1
  908. while test $((tet_l16_n += 1)) -lt $tet_l16_ndlines
  909. do
  910. eval ${tet_l16_smdata}_LINE[$tet_l16_n]='"${TET_REPLY_DATA_LINE[$((tet_l16_n + 1))]}"'
  911. done
  912. ;;
  913. esac
  914. fi
  915. ;;
  916. esac
  917. fi
  918. tet_kshapid_release
  919. case "$tet_reply_code" in
  920. OK)
  921. return 0
  922. ;;
  923. *)
  924. return 1
  925. ;;
  926. esac
  927. }
  928. # tet_kshapid_merror - call tet_merror() in the helper process
  929. # usage: tet_kshapid_merror lines ...
  930. tet_kshapid_merror()
  931. {
  932. tet_kshapid_request tet_merror $#
  933. while test $# -gt 0
  934. do
  935. tet_kshapid_request_data "$1"
  936. shift
  937. done
  938. tet_kshapid_check_reply tet_merror 0
  939. tet_kshapid_release
  940. return 0
  941. }
  942. # ******************************************************************
  943. #
  944. # functions to read from and write to the ksh API helper process
  945. #
  946. # tet_kshapid_request - send a request to the helper process
  947. # usage: tet_kshapid_request request args ...
  948. tet_kshapid_request()
  949. {
  950. # local variables
  951. typeset tet_l17_request=${1:?}
  952. # guard against a call while a request is already in progress;
  953. # this is only likely to happen if a request in progress is
  954. # interrupted by a signal and the handler makes another request
  955. : ${1:?}
  956. if test -z "$TET_KSHAPID_REQUEST"
  957. then
  958. TET_KSHAPID_REQUEST=$tet_l17_request
  959. else
  960. TET_KSHAPID_USABLE=no
  961. tet_fatal "unexpected $tet_l17_request request to helper" \
  962. "process while a $TET_KSHAPID_REQUEST request was" \
  963. "already in progress"
  964. fi
  965. tet_kshapid_write tet_request ${TET_API_VERSION:?} $*
  966. }
  967. # tet_kshapid_release - mark the end of a request to the helper process
  968. # usage: tet_kshapid_release
  969. tet_kshapid_release()
  970. {
  971. if test -z "$TET_KSHAPID_REQUEST"
  972. then
  973. : tet_kshapid_release: TET_KSHAPID_REQUEST was NULL
  974. fi
  975. TET_KSHAPID_REQUEST=
  976. }
  977. # tet_kshapid_request_data - send request data to the helper process
  978. # usage: tet_kshapid_request_data line ...
  979. tet_kshapid_request_data()
  980. {
  981. # local variables
  982. typeset tet_l18_line=
  983. # a newline
  984. typeset tet_l18_nl="
  985. "
  986. # convert embedded newlines to tabs
  987. tet_l18_line="$*"
  988. while test ${#tet_l18_line} -gt 0 -a -z "${tet_l18_line##*$tet_l18_nl*}"
  989. do
  990. tet_l18_line="${tet_l18_line%%$tet_l18_nl*} ${tet_l18_line#*$tet_l18_nl}"
  991. done
  992. tet_kshapid_write tet_request_data "$tet_l18_line"
  993. }
  994. # tet_kshapid_check_reply - read a reply from the helper process and check it
  995. # usage: tet_kshapid_check_reply request expected-argc [ok-code ...]
  996. # On return the number of paramaters on the reply line is in TET_REPLY_ARGC and
  997. # the parameters themselves are in the TET_REPLY_ARGV[] array.
  998. # The reply code is in tet_reply_code.
  999. # There is no return when the reply code is not OK or one of the
  1000. # named ok-codes.
  1001. # If an ok-code is ANY, then any reply code is acceptable.
  1002. tet_kshapid_check_reply()
  1003. {
  1004. # local variables
  1005. typeset -i tet_l19_n=0
  1006. typeset -i tet_l19_expected_argc=0
  1007. typeset tet_l19_kshapid_usable_save=
  1008. typeset tet_l19_line=
  1009. typeset tet_l19_ok_codes=OK
  1010. typeset tet_l19_bad_codes="MAGIC INTERN INVAL REQ"
  1011. typeset tet_l19_ok_reply=
  1012. typeset tet_l19_request=
  1013. typeset tet_l19_rc=
  1014. typeset tet_l19_tag=
  1015. typeset tet_l19_version=
  1016. # set a default reply code
  1017. tet_reply_code=INTERN
  1018. # process the function arguments
  1019. tet_l19_request=${1:?}
  1020. tet_l19_expected_argc=${2:?}
  1021. shift 2
  1022. while test $# -gt 0
  1023. do
  1024. case "$1" in
  1025. !*)
  1026. tet_l19_bad_codes="$tet_l19_bad_codes${tet_l19_bad_codes:+ }${1#?}"
  1027. ;;
  1028. *)
  1029. tet_l19_ok_codes="$tet_l19_ok_codes${tet_l19_ok_codes:+ }$1"
  1030. ;;
  1031. esac
  1032. shift
  1033. done
  1034. # read the reply line from the helper process
  1035. while :
  1036. do
  1037. tet_kshapid_read tet_l19_line
  1038. test $# -gt 0 && shift $#
  1039. set -- $tet_l19_line
  1040. if test tet_reply_data = "$1"
  1041. then
  1042. tet_l19_kshapid_usable_save="$TET_KSHAPID_USABLE"
  1043. TET_KSHAPID_USABLE=no
  1044. tet_merror "tet_kshapid_check_reply: discarded unexpected tet_reply_data line" \
  1045. "the line discarded was: \"$tet_l19_line\""
  1046. TET_KSHAPID_USABLE="$tet_l19_kshapid_usable_save"
  1047. else
  1048. break
  1049. fi
  1050. done
  1051. # check that the line contains the expected number of fields
  1052. : $((tet_l19_expected_argc += 3))
  1053. if test $# -lt $tet_l19_expected_argc
  1054. then
  1055. tet_kshapid_release
  1056. tet_mfatal \
  1057. "not enough fields in line read from ksh API helper process in reply to $tet_l19_request request" \
  1058. "expected $tet_l19_expected_argc fields but only read $# fields" \
  1059. "the line read was: \"$tet_l19_line\""
  1060. fi
  1061. # process the reply line paramaters
  1062. tet_l19_tag="$1"
  1063. tet_l19_version="$2"
  1064. tet_reply_code="$3"
  1065. shift 3
  1066. TET_REPLY_ARGC=$#
  1067. unset TET_REPLY_ARGV
  1068. tet_l19_n=-1
  1069. while test $# -gt 0
  1070. do
  1071. TET_REPLY_ARGV[$((tet_l19_n += 1))]=$1
  1072. shift
  1073. done
  1074. # check the line type
  1075. # to make sure that we are in sync with the helper process
  1076. if test tet_reply != "$tet_l19_tag"
  1077. then
  1078. TET_KSHAPID_USABLE=no
  1079. tet_mfatal \
  1080. "read unexpected line type from ksh API helper process in reply to $tet_l19_request request" \
  1081. "expected \"tet_reply ...\", encountered \"$tet_l19_tag\"" \
  1082. "the line read was: \"$tet_l19_line\""
  1083. fi
  1084. # check the version string
  1085. # this check should never fail since the helper process should have
  1086. # checked our version string in the request
  1087. if test ${TET_API_VERSION:?} != "$tet_l19_version"
  1088. then
  1089. TET_KSHAPID_USABLE=no
  1090. tet_mfatal \
  1091. "API version mismatch in line read from ksh API helper process in reply to $tet_l19_request request" \
  1092. "expected version ${TET_API_VERSION:?}, encountered version $tet_l19_version" \
  1093. "the line read was: \"$tet_l19_line\""
  1094. fi
  1095. # check that the reply code is one of the ones that we expect
  1096. tet_l19_ok_reply=
  1097. for tet_l19_rc in $tet_l19_bad_codes
  1098. do
  1099. if test $tet_l19_rc = "$tet_reply_code"
  1100. then
  1101. tet_l19_ok_reply=no
  1102. break
  1103. fi
  1104. done
  1105. if test -z "$tet_l19_ok_reply"
  1106. then
  1107. for tet_l19_rc in $tet_l19_ok_codes
  1108. do
  1109. if test $tet_l19_rc = "$tet_reply_code" -o $tet_l19_rc = ANY
  1110. then
  1111. tet_l19_ok_reply=yes
  1112. break
  1113. fi
  1114. done
  1115. fi
  1116. if test $tet_l19_ok_reply != yes
  1117. then
  1118. tet_kshapid_release
  1119. tet_fatal "ksh API helper process returned unexpected reply" \
  1120. "$tet_reply_code to $tet_l19_request request"
  1121. fi
  1122. }
  1123. # tet_kshapid_read_reply_data - read a set of reply data lines
  1124. # from the helper process
  1125. # usage: tet_kshapid_read_reply_data nlines
  1126. # On return, nlines of reply data are stored in
  1127. # the TET_REPLY_DATA_LINE[] array.
  1128. tet_kshapid_read_reply_data()
  1129. {
  1130. # local variables
  1131. typeset -i tet_l20_lcount=-1
  1132. typeset -i tet_l20_nlines=${1:?}
  1133. unset TET_REPLY_DATA_LINE
  1134. while test $((tet_l20_lcount += 1)) -lt $tet_l20_nlines
  1135. do
  1136. tet_kshapid_read_data_line $tet_l20_lcount
  1137. done
  1138. }
  1139. # tet_kshapid_read_data_line - read a single reply data line
  1140. # from the helper process
  1141. # usage: tet_kshapid_read_data_line index
  1142. # On return, the reply data line is stored in TET_REPLY_DATA_LINE[index]
  1143. tet_kshapid_read_data_line()
  1144. {
  1145. # local variables
  1146. typeset -i tet_l21_index=${1:?}
  1147. typeset tet_l21_line=
  1148. # read the reply line from the helper process
  1149. tet_kshapid_read tet_l21_line
  1150. test $# -gt 0 && shift $#
  1151. set -- $tet_l21_line
  1152. # check that the line contains at least the tet_reply_data tag
  1153. if test $# -lt 1 -o "$1" != tet_reply_data
  1154. then
  1155. TET_KSHAPID_USABLE=no
  1156. tet_mfatal \
  1157. "data line read from the ksh API helper process did not start with a \"tet_reply_data\" tag" \
  1158. "the line read was: \"$tet_l21_line\""
  1159. fi
  1160. # store the line without the tag
  1161. tet_l21_line="${tet_l21_line#tet_reply_data}"
  1162. TET_REPLY_DATA_LINE[$tet_l21_index]="${tet_l21_line# }"
  1163. }
  1164. # tet_kshapid_read - read a line from the helper process
  1165. # on return the line is left in the named variable
  1166. # usage: tet_kshapid_read variable
  1167. tet_kshapid_read()
  1168. {
  1169. eval ${1:?}=
  1170. if eval read -p -r $1
  1171. then
  1172. : ok
  1173. else
  1174. TET_KSHAPID_USABLE=no
  1175. tet_fatal "read error from ksh API helper process"
  1176. fi
  1177. }
  1178. # tet_kshapid_write - write a line to the helper process
  1179. # usage: tet_kshapid_write line ...
  1180. tet_kshapid_write()
  1181. {
  1182. if print -p -R "$*"
  1183. then
  1184. : ok
  1185. else
  1186. TET_KSHAPID_USABLE=no
  1187. tet_mfatal "write error to ksh API helper process" \
  1188. "the line that could not be written is: \"$*\""
  1189. fi
  1190. }
  1191. # ******************************************************************
  1192. #
  1193. # utility functions
  1194. #
  1195. # tet_sp2us - convert spaces to underscores
  1196. # usage: tet_sp2us text ...
  1197. #
  1198. # it would be easier to use ${variable//pattern/replacement} but this
  1199. # construct isn't supported by the older versions of ksh
  1200. tet_sp2us()
  1201. {
  1202. typeset tet_l24_arg="$*"
  1203. while :
  1204. do
  1205. case "$tet_l24_arg" in
  1206. *[\ \ ]*)
  1207. tet_l24_arg="${tet_l24_arg%%[ ]*}_${tet_l24_arg#*[ ]}"
  1208. ;;
  1209. *)
  1210. break
  1211. ;;
  1212. esac
  1213. done
  1214. print -R $tet_l24_arg
  1215. }
  1216. #
  1217. # ******************************************************************
  1218. #
  1219. # ensure that we have been invoked in the environment provided by tcc/tccd
  1220. if test -z "$TET_TIARGS"
  1221. then
  1222. tet_mfatal "TET_TIARGS is not set or NULL" \
  1223. "A process that use the Distributed ksh API cannot be run stand-alone -" \
  1224. "It must be run under the control of tcc"
  1225. fi
  1226. # set up TET_API_VERSION, changing any embedded spaces to underscores
  1227. typeset -r TET_API_VERSION=`tet_sp2us 3.8`