From 3950ffe2a485479f6561c27364d3d7df5a21d124 Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sun, 24 Jun 2012 22:28:35 +0000 Subject: Imported Upstream version 93u+ --- src/lib/libcoshell/Makefile | 20 ++ src/lib/libcoshell/Mamfile | 464 ++++++++++++++++++++++++++++++++++++++++++ src/lib/libcoshell/RELEASE | 49 +++++ src/lib/libcoshell/coclose.c | 113 ++++++++++ src/lib/libcoshell/codata.c | 169 +++++++++++++++ src/lib/libcoshell/coexec.c | 449 ++++++++++++++++++++++++++++++++++++++++ src/lib/libcoshell/coexport.c | 80 ++++++++ src/lib/libcoshell/coinit.c | 431 +++++++++++++++++++++++++++++++++++++++ src/lib/libcoshell/cokill.c | 134 ++++++++++++ src/lib/libcoshell/colib.h | 137 +++++++++++++ src/lib/libcoshell/coopen.c | 411 +++++++++++++++++++++++++++++++++++++ src/lib/libcoshell/coquote.c | 60 ++++++ src/lib/libcoshell/coshell.3 | 396 +++++++++++++++++++++++++++++++++++ src/lib/libcoshell/coshell.h | 143 +++++++++++++ src/lib/libcoshell/costash.c | 40 ++++ src/lib/libcoshell/cosync.c | 127 ++++++++++++ src/lib/libcoshell/cowait.c | 411 +++++++++++++++++++++++++++++++++++++ src/lib/libcoshell/ignore.sh | 45 ++++ src/lib/libcoshell/procrun.c | 56 +++++ src/lib/libcoshell/silent.sh | 44 ++++ src/lib/libcoshell/system.c | 57 ++++++ 21 files changed, 3836 insertions(+) create mode 100644 src/lib/libcoshell/Makefile create mode 100644 src/lib/libcoshell/Mamfile create mode 100644 src/lib/libcoshell/RELEASE create mode 100644 src/lib/libcoshell/coclose.c create mode 100644 src/lib/libcoshell/codata.c create mode 100644 src/lib/libcoshell/coexec.c create mode 100644 src/lib/libcoshell/coexport.c create mode 100644 src/lib/libcoshell/coinit.c create mode 100644 src/lib/libcoshell/cokill.c create mode 100644 src/lib/libcoshell/colib.h create mode 100644 src/lib/libcoshell/coopen.c create mode 100644 src/lib/libcoshell/coquote.c create mode 100644 src/lib/libcoshell/coshell.3 create mode 100644 src/lib/libcoshell/coshell.h create mode 100644 src/lib/libcoshell/costash.c create mode 100644 src/lib/libcoshell/cosync.c create mode 100644 src/lib/libcoshell/cowait.c create mode 100644 src/lib/libcoshell/ignore.sh create mode 100644 src/lib/libcoshell/procrun.c create mode 100644 src/lib/libcoshell/silent.sh create mode 100644 src/lib/libcoshell/system.c (limited to 'src/lib/libcoshell') diff --git a/src/lib/libcoshell/Makefile b/src/lib/libcoshell/Makefile new file mode 100644 index 0000000..8fe9beb --- /dev/null +++ b/src/lib/libcoshell/Makefile @@ -0,0 +1,20 @@ +/* + * coshell library + */ + +:PACKAGE: ast + +CCFLAGS = $(CC.OPTIMIZE) $(CC.PIC) /* pic allows archive to be pulled into other dlls */ + +LICENSE = since=1990,author=gsf + +coshell 1.0 :LIBRARY: RELEASE coshell.3 coshell.h colib.h \ + coopen.c coclose.c coinit.c coexec.c costash.c \ + cowait.c cokill.c cosync.c coquote.c codata.c \ + coexport.c procrun.c system.c + +ignore :: ignore.sh + +silent :: silent.sh + +$(INCLUDEDIR) :INSTALLPROTO: coshell.h diff --git a/src/lib/libcoshell/Mamfile b/src/lib/libcoshell/Mamfile new file mode 100644 index 0000000..6f67e24 --- /dev/null +++ b/src/lib/libcoshell/Mamfile @@ -0,0 +1,464 @@ +info mam static 00000 1994-07-17 make (AT&T Research) 5.7 2012-02-29 +setv INSTALLROOT ../../.. +setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast +setv PACKAGE_ast_LIB ${INSTALLROOT}/lib +setv PACKAGEROOT ../../../../.. +setv AR ${mam_cc_AR} ${mam_cc_AR_ARFLAGS} +setv ARFLAGS rc +setv AS as +setv ASFLAGS +setv CC cc +setv mam_cc_FLAGS ${mam_cc_PIC} +setv CCFLAGS ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?} +setv CCLDFLAGS ${-strip-symbols?1?${mam_cc_LD_STRIP}??} +setv COTEMP $$ +setv CPIO cpio +setv CPIOFLAGS +setv CPP "${CC} -E" +setv F77 f77 +setv HOSTCC ${CC} +setv IGNORE +setv LD ld +setv LDFLAGS +setv LEX lex +setv LEXFLAGS +setv LPR lpr +setv LPRFLAGS +setv M4FLAGS +setv NMAKE nmake +setv NMAKEFLAGS +setv PR pr +setv PRFLAGS +setv SHELL /bin/sh +setv SILENT +setv TAR tar +setv YACC yacc +setv YACCFLAGS -d +make ${PACKAGEROOT}/lib/package/ast.lic +done ${PACKAGEROOT}/lib/package/ast.lic +make install +make coshell +make libcoshell.a archive +make coshell.req +exec - set - +exec - echo 'int main(){return 0;}' > 1.${COTEMP}.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -c 1.${COTEMP}.c && +exec - x=`${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l'*' 2>&1 | sed -e 's/[][()+@?]/#/g' || :` && +exec - { +exec - case "" in +exec - *?) echo " " ;; +exec - esac +exec - for i in coshell ast +exec - do case $i in +exec - "coshell"|coshell) +exec - ;; +exec - *) if test -f ${INSTALLROOT}/lib/lib/$i +exec - then y=`cat ${INSTALLROOT}/lib/lib/$i` +exec - case $y in +exec - *-?*) echo "" $y ;; +exec - esac +exec - continue +exec - elif test ! -f ${INSTALLROOT}/lib/lib$i.a +exec - then case `{ ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -L${INSTALLROOT}/lib ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l$i 2>&1 || echo '' $x ;} | sed -e 's/[][()+@?]/#/g' || :` in +exec - *$x*) case `{ ${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l$i 2>&1 || echo '' $x ;} | sed -e 's/[][()+@?]/#/g' || :` in +exec - *$x*) continue ;; +exec - esac +exec - ;; +exec - esac +exec - fi +exec - ;; +exec - esac +exec - echo " -l$i" +exec - done +exec - } > coshell.req +exec - rm -f 1.${COTEMP}.* +done coshell.req generated +make coopen.o +make coopen.c +make ${PACKAGE_ast_INCLUDE}/tok.h implicit +make ${PACKAGE_ast_INCLUDE}/ast.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_api.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_api.h dontcare +make ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_map.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_map.h dontcare +make ${PACKAGE_ast_INCLUDE}/endian.h implicit +make ${PACKAGE_ast_INCLUDE}/bytesex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +done ${PACKAGE_ast_INCLUDE}/bytesex.h dontcare +done ${PACKAGE_ast_INCLUDE}/endian.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_common.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +make ${PACKAGE_ast_INCLUDE}/regex.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit +make ${PACKAGE_ast_INCLUDE}/wctype.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_wctype.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_wctype.h dontcare +done ${PACKAGE_ast_INCLUDE}/wctype.h dontcare +make ${PACKAGE_ast_INCLUDE}/stdio.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_stdio.h implicit +make ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit +done ${PACKAGE_ast_INCLUDE}/sfio_s.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_stdio.h dontcare +done ${PACKAGE_ast_INCLUDE}/stdio.h dontcare +prev ${PACKAGE_ast_INCLUDE}/stdio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_wchar.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +make ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +make ${INSTALLROOT}/include/prototyped.h implicit +done ${INSTALLROOT}/include/prototyped.h dontcare +done ${PACKAGE_ast_INCLUDE}/prototyped.h dontcare +done ${PACKAGE_ast_INCLUDE}/regex.h dontcare +make ${PACKAGE_ast_INCLUDE}/getopt.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_getopt.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/getopt.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_map.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_botch.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_botch.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_limits.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_limits.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_fcntl.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_fs.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_fcntl.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_sys.h implicit +prev ${PACKAGE_ast_INCLUDE}/getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_sys.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_lib.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_lib.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_std.h dontcare +done ${PACKAGE_ast_INCLUDE}/vmalloc.h dontcare +make ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +done ${PACKAGE_ast_INCLUDE}/sfio.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/tok.h +make ${PACKAGE_ast_INCLUDE}/sfdisc.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/sfdisc.h +make ${PACKAGE_ast_INCLUDE}/proc.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/proc.h +make ${PACKAGE_ast_INCLUDE}/namval.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/namval.h +make colib.h implicit +make ${PACKAGE_ast_INCLUDE}/wait.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_wait.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_wait.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/wait.h dontcare +make ${PACKAGE_ast_INCLUDE}/sig.h implicit +done ${PACKAGE_ast_INCLUDE}/sig.h dontcare +make ${PACKAGE_ast_INCLUDE}/error.h implicit +make ${PACKAGE_ast_INCLUDE}/option.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/option.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/error.h dontcare +make coshell.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done coshell.h dontcare +prev ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit +make ${PACKAGE_ast_INCLUDE}/dt.h implicit +prev ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit +make ${PACKAGE_ast_INCLUDE}/cdt.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +done ${PACKAGE_ast_INCLUDE}/cdt.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/dt.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done colib.h +done coopen.c +meta coopen.o %.c>%.o coopen.c coopen +prev coopen.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coopen.c +done coopen.o generated +make coclose.o +make coclose.c +prev colib.h implicit +done coclose.c +meta coclose.o %.c>%.o coclose.c coclose +prev coclose.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coclose.c +done coclose.o generated +make coinit.o +make coinit.c +make ${PACKAGE_ast_INCLUDE}/ls.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_mode.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ls.h +make ${PACKAGE_ast_INCLUDE}/fs3d.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/fs3d.h +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev colib.h implicit +done coinit.c +meta coinit.o %.c>%.o coinit.c coinit +prev coinit.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coinit.c +done coinit.o generated +make coexec.o +make coexec.c +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/proc.h implicit +prev colib.h implicit +done coexec.c +meta coexec.o %.c>%.o coexec.c coexec +prev coexec.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coexec.c +done coexec.o generated +make costash.o +make costash.c +prev colib.h implicit +done costash.c +meta costash.o %.c>%.o costash.c costash +prev costash.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c costash.c +done costash.o generated +make cowait.o +make cowait.c +prev colib.h implicit +done cowait.c +meta cowait.o %.c>%.o cowait.c cowait +prev cowait.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c cowait.c +done cowait.o generated +make cokill.o +make cokill.c +prev colib.h implicit +done cokill.c +meta cokill.o %.c>%.o cokill.c cokill +prev cokill.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c cokill.c +done cokill.o generated +make cosync.o +make cosync.c +make FEATURE/nfsd implicit +meta FEATURE/nfsd >FEATURE/% nfsd +bind -last +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libast} : def nfsd +done FEATURE/nfsd generated +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev colib.h implicit +done cosync.c +meta cosync.o %.c>%.o cosync.c cosync +prev cosync.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c cosync.c +done cosync.o generated +make coquote.o +make coquote.c +prev colib.h implicit +done coquote.c +meta coquote.o %.c>%.o coquote.c coquote +prev coquote.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coquote.c +done coquote.o generated +make codata.o +make codata.c +prev colib.h implicit +done codata.c +meta codata.o %.c>%.o codata.c codata +prev codata.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c codata.c +done codata.o generated +make coexport.o +make coexport.c +prev colib.h implicit +done coexport.c +meta coexport.o %.c>%.o coexport.c coexport +prev coexport.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c coexport.c +done coexport.o generated +make procrun.o +make procrun.c +prev ${PACKAGE_ast_INCLUDE}/proc.h implicit +prev colib.h implicit +done procrun.c +meta procrun.o %.c>%.o procrun.c procrun +prev procrun.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c procrun.c +done procrun.o generated +make system.o +make system.c +prev colib.h implicit +done system.c +meta system.o %.c>%.o system.c system +prev system.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_coshell -c system.c +done system.o generated +exec - ${AR} rc libcoshell.a coopen.o coclose.o coinit.o coexec.o costash.o cowait.o cokill.o cosync.o coquote.o codata.o coexport.o procrun.o system.o +exec - (ranlib libcoshell.a) >/dev/null 2>&1 || true +done libcoshell.a generated +done coshell virtual +prev libcoshell.a archive +make ignore +make ignore.sh +done ignore.sh +meta ignore %.sh>% ignore.sh ignore +prev ignore.sh +exec - case static,port:$OPTIND:$RANDOM in +exec - ?*:*:*|*::*|*:*:$RANDOM) +exec - ;; +exec - *) if ENV= LC_ALL=C x= $SHELL -nc '[[ a || b ]] && : ${list[level]} !(pattern)' 2>/dev/null +exec - then if grep -q '### .*archaic.* ###' +exec - then : ignore contains archaic constructs : +exec - else ENV= LC_ALL=C $SHELL -n ignore.sh +exec - fi +exec - fi +exec - ;; +exec - esac +exec - case '${mam_cc_SHELLMAGIC}' in +exec - "") case 0 in +exec - 0) cp ignore.sh ignore +exec - ;; +exec - *) { +exec - i=`(read x; echo $x) < ignore.sh` +exec - case $i in +exec - '#!'*|*'||'*|':'*|'":"'*|"':'"*) echo "$i" ;; +exec - esac +exec - cat - ignore.sh <<'!' +exec - +exec - ! +exec - } > ignore +exec - ;; +exec - esac +exec - ;; +exec - *) cat - ignore.sh > ignore <<'!' +exec - ${mam_cc_SHELLMAGIC} +exec - +exec - ! +exec - ;; +exec - esac +exec - silent test -w ignore -a -x ignore || chmod u+w,+x ignore +done ignore generated +make silent +make silent.sh +done silent.sh +meta silent %.sh>% silent.sh silent +prev silent.sh +exec - case static,port:$OPTIND:$RANDOM in +exec - ?*:*:*|*::*|*:*:$RANDOM) +exec - ;; +exec - *) if ENV= LC_ALL=C x= $SHELL -nc '[[ a || b ]] && : ${list[level]} !(pattern)' 2>/dev/null +exec - then if grep -q '### .*archaic.* ###' +exec - then : silent contains archaic constructs : +exec - else ENV= LC_ALL=C $SHELL -n silent.sh +exec - fi +exec - fi +exec - ;; +exec - esac +exec - case '${mam_cc_SHELLMAGIC}' in +exec - "") case 0 in +exec - 0) cp silent.sh silent +exec - ;; +exec - *) { +exec - i=`(read x; echo $x) < silent.sh` +exec - case $i in +exec - '#!'*|*'||'*|':'*|'":"'*|"':'"*) echo "$i" ;; +exec - esac +exec - cat - silent.sh <<'!' +exec - +exec - ! +exec - } > silent +exec - ;; +exec - esac +exec - ;; +exec - *) cat - silent.sh > silent <<'!' +exec - ${mam_cc_SHELLMAGIC} +exec - +exec - ! +exec - ;; +exec - esac +exec - silent test -w silent -a -x silent || chmod u+w,+x silent +done silent generated +make ${INSTALLROOT}/lib +exec - if silent test ! -d ${INSTALLROOT}/lib +exec - then mkdir -p ${INSTALLROOT}/lib +exec - fi +done ${INSTALLROOT}/lib generated +make ${INSTALLROOT}/lib/libcoshell.a archive +prev ${INSTALLROOT}/lib +prev libcoshell.a archive +exec - test '' = 'libcoshell.a' || ${STDCMP} 2>/dev/null -s libcoshell.a ${INSTALLROOT}/lib/libcoshell.a || { ${STDMV} ${INSTALLROOT}/lib/libcoshell.a ${INSTALLROOT}/lib/libcoshell.a.old 2>/dev/null || true; ${STDCP} libcoshell.a ${INSTALLROOT}/lib/libcoshell.a ;} +exec - (ranlib ${INSTALLROOT}/lib/libcoshell.a) >/dev/null 2>&1 || true +done ${INSTALLROOT}/lib/libcoshell.a generated +make ${INSTALLROOT}/man/man3 +exec - if silent test ! -d ${INSTALLROOT}/man/man3 +exec - then mkdir -p ${INSTALLROOT}/man/man3 +exec - fi +done ${INSTALLROOT}/man/man3 generated +make ${INSTALLROOT}/man/man3/coshell.3 +prev ${INSTALLROOT}/man/man3 +make coshell.3 +done coshell.3 +exec - test '' = 'coshell.3' || ${STDCMP} 2>/dev/null -s coshell.3 ${INSTALLROOT}/man/man3/coshell.3 || { ${STDMV} ${INSTALLROOT}/man/man3/coshell.3 ${INSTALLROOT}/man/man3/coshell.3.old 2>/dev/null || true; ${STDCP} coshell.3 ${INSTALLROOT}/man/man3/coshell.3 ;} +done ${INSTALLROOT}/man/man3/coshell.3 generated +make ${INSTALLROOT}/lib/lib +exec - if silent test ! -d ${INSTALLROOT}/lib/lib +exec - then mkdir -p ${INSTALLROOT}/lib/lib +exec - fi +done ${INSTALLROOT}/lib/lib generated +make ${INSTALLROOT}/lib/lib/coshell +prev ${INSTALLROOT}/lib/lib +prev coshell.req +exec - test '' = 'coshell.req' || ${STDCMP} 2>/dev/null -s coshell.req ${INSTALLROOT}/lib/lib/coshell || { ${STDMV} ${INSTALLROOT}/lib/lib/coshell ${INSTALLROOT}/lib/lib/coshell.old 2>/dev/null || true; ${STDCP} coshell.req ${INSTALLROOT}/lib/lib/coshell ;} +done ${INSTALLROOT}/lib/lib/coshell generated +make ${INSTALLROOT}/bin +exec - if silent test ! -d ${INSTALLROOT}/bin +exec - then mkdir -p ${INSTALLROOT}/bin +exec - fi +done ${INSTALLROOT}/bin generated +make ${INSTALLROOT}/bin/ignore +prev ${INSTALLROOT}/bin +prev ignore +exec - test '' = 'ignore' || ${STDCMP} 2>/dev/null -s ignore ${INSTALLROOT}/bin/ignore || { ${STDMV} ${INSTALLROOT}/bin/ignore ${INSTALLROOT}/bin/ignore.old 2>/dev/null || true; ${STDCP} ignore ${INSTALLROOT}/bin/ignore ;} +done ${INSTALLROOT}/bin/ignore generated +make ${INSTALLROOT}/bin/silent +prev silent +exec - test '' = 'silent' || ${STDCMP} 2>/dev/null -s silent ${INSTALLROOT}/bin/silent || { ${STDMV} ${INSTALLROOT}/bin/silent ${INSTALLROOT}/bin/silent.old 2>/dev/null || true; ${STDCP} silent ${INSTALLROOT}/bin/silent ;} +done ${INSTALLROOT}/bin/silent generated +make ${PACKAGE_ast_INCLUDE} +exec - if silent test ! -d ${PACKAGE_ast_INCLUDE} +exec - then mkdir -p ${PACKAGE_ast_INCLUDE} +exec - fi +done ${PACKAGE_ast_INCLUDE} generated +make ${PACKAGE_ast_INCLUDE}/coshell.h +prev ${PACKAGE_ast_INCLUDE} +prev coshell.h +exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1990,author=gsf' coshell.h > 1.${COTEMP}.x +exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/coshell.h 1.${COTEMP}.x +exec - then rm -f 1.${COTEMP}.x +exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/coshell.h +exec - fi +done ${PACKAGE_ast_INCLUDE}/coshell.h generated +done install virtual +make test +done test dontcare virtual diff --git a/src/lib/libcoshell/RELEASE b/src/lib/libcoshell/RELEASE new file mode 100644 index 0000000..9d09db2 --- /dev/null +++ b/src/lib/libcoshell/RELEASE @@ -0,0 +1,49 @@ +12-02-22 coinit.c: handle non-identifier export var names +11-12-13 cowait.c: handle sfpoll() error return on interrupt +11-11-21 cowait.c: poll before blocking read to weed out killed jobs (no 'x' message) +11-08-30 codata.c,coopen.c: drop macro "..." catenation for old cc +10-08-11 coinit.c: force _BLD_DLL for environ intercept +10-06-01 sync with ast api 20100601 +10-05-19 cokill.c: do cowait(co,co,0) to drain pending messages +10-05-15 coshell.h,coopen.c: add CO_ORPHAN for PROC_ORPHAN +10-05-11 coopen.c: add PROC_ORPHAN for CO_SHELL +10-05-10 coopen.c: no atexit() for CO_SHELL +10-04-15 first ksh93u local job pool tests work (service daemon tbd) +10-04-14 cowait.c: add 3rd cowait() arg timeout; 0 Coshell_t* operates on all open coshells +10-04-10 coshell.h: add CO_SHELL for shell using coshell! +09-12-09 coexport.c: add runtime CO_ENV_EXPORT hook that avoids changing environ +08-10-28 coopen.c: close write side of parent msgfd -- doh +08-04-28 coexec.c: check for fd 1,2 equivalence before CO_SERIALIZE 2>&1 +07-10-29 coshell.h,coexec.c: fix procrun()/system() intercept logic +07-08-15 add CO_SEPARATE,CO_MODE_SEPARATE for separate shell+wait per action +07-04-09 Makefile: $(CC.PIC) to allow archive to be pulled into other dlls +06-08-22 coshell.h: procrun => coprocrun, system => cosystem +06-08-09 coshell.h: export CO_ENV_MSGFD for COSHELL=coshell +06-08-02 coexec.c: Cojob_t.flags&CO_SERVICE for service requests +06-08-02 cokill.c: cokill() signal==0 => kill CO_SERVICE jobs +06-07-27 coexec.c: drop server cowait() that bypassed caller +06-06-21 coexec.c: add non-block cowait() to drain responses +06-06-11 fix service intercept cleanup +06-05-24 add service=name:init lightweight service intercepts +05-04-19 cowait.c: beef up invalid message tests and diagnostics +05-04-11 drop fixed CO_MSGFD for $_coshell_msgfd +05-04-07 coexec.c: fix !_lib_fork&&_map_spawnve close-on-exec redirection +04-09-22 cowait.c: remove CO_SERIALIZE temporaries after listing -- duh +04-09-01 co*: add CO_SERIALIZE +04-07-22 system.c: access() => eaccess() +04-02-11 coinit.c: fix CO_CROSS PATH initialization +02-10-30 coclose.c: fix reference-after-free bug in coclose() +02-01-31 codata.c,coopen.c: fix CO_MSGFD parameterization +02-01-24 coopen.c: fix small memory leak +01-10-26 coopen.c: hung sfclose(fp) -> close(sffileno(fp)) -- wow +01-09-11 coinit.c: fix coident[] for ancient bsh that die on `test == 1' + coinit.c: and fix coident[] to weed out buggy ksh88i trap on exit +01-05-31 co*: add CO_CROSS, expose CO_DEVFD +01-04-23 coquote: add state.type to avoid getenv() overwrite on some systems +01-01-01 cokill: killjob => cokilljob, killshell => cokillshell +00-12-18 coinit: CO_OSH ? "${!-$$}" : "${!:-$$}" +00-10-25 codata: $ZSH_VERSION is not ksh +00-02-14 procrun,system: system(3) returns wait() status (not shell status) +99-11-19 co*: add CO_OSH for bsdi lack of times(1) + coexec: CO_IGNORE for all but real ksh +98-06-22 coinit: quote cd path arg diff --git a/src/lib/libcoshell/coclose.c b/src/lib/libcoshell/coclose.c new file mode 100644 index 0000000..9ca00b5 --- /dev/null +++ b/src/lib/libcoshell/coclose.c @@ -0,0 +1,113 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * close a coshell + */ + +#include "colib.h" + +/* + * called when coshell is hung + */ + +static void +hung(int sig) +{ + NoP(sig); + kill(state.current->pid, SIGKILL); +} + +/* + * shut down one coshell + */ + +static int +shut(register Coshell_t* co) +{ + register Coshell_t* cs; + int n; + int status; + Coshell_t* ps; + Coservice_t* sv; + Sig_handler_t handler; + + sfclose(co->msgfp); + close(co->cmdfd); + if (co->pid) + { + if (co->running > 0) + killpg(co->pid, SIGTERM); + state.current = co; + handler = signal(SIGALRM, hung); + n = alarm(3); + if (waitpid(co->pid, &status, 0) != co->pid) + status = -1; + alarm(n); + signal(SIGALRM, handler); + killpg(co->pid, SIGTERM); + } + else + status = 0; + if (co->flags & CO_DEBUG) + errormsg(state.lib, 2, "coshell %d jobs %d user %s sys %s", co->index, co->total, fmtelapsed(co->user, CO_QUANT), fmtelapsed(co->sys, CO_QUANT)); + for (sv = co->service; sv; sv = sv->next) + { + if (sv->fd > 0) + close(sv->fd); + if (sv->pid) + waitpid(sv->pid, &status, 0); + } + cs = state.coshells; + ps = 0; + while (cs) + { + if (cs == co) + { + cs = cs->next; + if (ps) + ps->next = cs; + else + state.coshells = cs; + vmclose(co->vm); + break; + } + ps = cs; + cs = cs->next; + } + return status; +} + +/* + * close coshell co + */ + +int +coclose(register Coshell_t* co) +{ + if (co) + return shut(co); + while (state.coshells) + shut(state.coshells); + return 0; +} diff --git a/src/lib/libcoshell/codata.c b/src/lib/libcoshell/codata.c new file mode 100644 index 0000000..fa6559f --- /dev/null +++ b/src/lib/libcoshell/codata.c @@ -0,0 +1,169 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * coshell readonly data + */ + +#include "colib.h" + +char coident[] = "\ +# @(#)$Id: libcoshell (AT&T Research) 2012-02-22 $\n\ +%s=%d\n\ +{ { (eval 'function fun { trap \":\" 0; return 1; }; trap \"exit 0\" 0; fun; exit 1') && PATH= print -u$%s ksh; } || { times && echo bsh >&$%s; } || { echo osh >&$%s; }; } >/dev/null 2>&1\n\ +"; + +char cobinit[] = "\ +if (eval 'f() echo') >/dev/null 2>&1\n\ +then eval 'ignore() {\n\ + case $- in\n\ + *x*) set -\n\ + _coshell_silent=\n\ + ;;\n\ + *) _coshell_silent=1\n\ + ;;\n\ + esac\n\ + _coshell_state=exp\n\ + _coshell_stop=\"<< -- StoP -- >>\"\n\ + _coshell_quote='\\\\\\''\n\ + set \"$@\" \"$_coshell_stop\"\n\ + while :\n\ + do case $1 in\n\ + $_coshell_stop)\n\ + shift\n\ + break\n\ + ;;\n\ + *=*) ;;\n\ + *) _coshell_state=arg ;;\n\ + esac\n\ + case $_coshell_state in\n\ + exp) _coshell_arg=`echo $1 | sed \"s/\\\\([^=]*\\\\)=\\\\(.*\\\\)/\\\\1=$_coshell_quote\\\\2$_coshell_quote/\"`\n\ + set \"\" \"$@\" \"$_coshell_arg\"\n\ + shift\n\ + ;;\n\ + arg) set \"\" \"$@\" \"$_coshell_quote$1$_coshell_quote\"\n\ + shift\n\ + ;;\n\ + esac\n\ + shift\n\ + done\n\ + case $_coshell_silent in\n\ + \"\") set \"set -x;\" \"$@\" ;;\n\ + esac\n\ + eval \"$@\"\n\ + return 0\n\ + }'\n\ + eval 'silent() {\n\ + case $- in\n\ + *x*) set -\n\ + _coshell_silent=\n\ + ;;\n\ + *) _coshell_silent=1\n\ + ;;\n\ + esac\n\ + _coshell_state=exp\n\ + _coshell_stop=\"<< -- StoP -- >>\"\n\ + _coshell_quote='\\\\\\''\n\ + set \"$@\" \"$_coshell_stop\"\n\ + while :\n\ + do case $1 in\n\ + $_coshell_stop)\n\ + shift\n\ + break\n\ + ;;\n\ + *=*) ;;\n\ + *) _coshell_state=arg ;;\n\ + esac\n\ + case $_coshell_state in\n\ + exp) _coshell_arg=`echo $1 | sed \"s/\\\\([^=]*\\\\)=\\\\(.*\\\\)/\\\\1=$_coshell_quote\\\\2$_coshell_quote/\"`\n\ + set \"\" \"$@\" \"$_coshell_arg\"\n\ + shift\n\ + ;;\n\ + arg) set \"\" \"$@\" \"$_coshell_quote$1$_coshell_quote\"\n\ + shift\n\ + ;;\n\ + esac\n\ + shift\n\ + done\n\ + eval \"$@\"\n\ + _coshell_state=$?\n\ + case $_coshell_silent in\n\ + \"\") set -x ;;\n\ + esac\n\ + return $_coshell_state\n\ + }'\n\ +else :\n\ +fi\n\ +"; + +char cokinit[] = "\ +set +o bgnice -o monitor\n\ +(wait $$; exit 0) 2>/dev/null || alias wait=:\n\ +alias ignore='ignore '\n\ +function ignore\n\ +{\n\ + integer argc=0\n\ + typeset argv state=exp\n\ + while :\n\ + do case $# in\n\ + 0) break ;;\n\ + esac\n\ + case $1 in\n\ + *=*) ;;\n\ + *) state=arg ;;\n\ + esac\n\ + case $state in\n\ + exp) argv[argc]=${1%%=*}=\"'${1#*=}'\" ;;\n\ + arg) argv[argc]=\"'\"$1\"'\" ;;\n\ + esac\n\ + ((argc=argc+1))\n\ + shift\n\ + done\n\ + eval \"${argv[@]}\"\n\ + return 0\n\ +}\n\ +alias silent='set +x X$- \"$@\";_coshell_flags_=$1;shift;silent '\n\ +function silent\n\ +{\n\ + case $_coshell_flags_ in\n\ + *x*) trap ' _coshell_status_=$?\n\ + if ((_coshell_status_==0))\n\ + then set -x\n\ + else set -x;(set +x;exit $_coshell_status_)\n\ + fi' 0\n\ + ;;\n\ + esac\n\ + \"$@\"\n\ +}\n\ +typeset -xf ignore silent\n\ +"; + +char* co_export[] = /* default export var list */ +{ + CO_ENV_EXPORT, /* first */ + CO_ENV_ATTRIBUTES, + CO_ENV_PROC, + "FPATH", + "VPATH", + 0 /* last */ +}; diff --git a/src/lib/libcoshell/coexec.c b/src/lib/libcoshell/coexec.c new file mode 100644 index 0000000..637c650 --- /dev/null +++ b/src/lib/libcoshell/coexec.c @@ -0,0 +1,449 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * send an action to the coshell for execution + */ + +#include "colib.h" + +#include +#include + +static Cojob_t* +service(register Coshell_t* co, Coservice_t* cs, Cojob_t* cj, int flags, Sfio_t* sp) +{ + Proc_t* proc; + size_t n; + int i; + int j; + int fds[2]; + long ops[4]; + char* s; + char** a; + + if (flags & CO_DEBUG) + { + for (a = cs->argv; *a; a++) + sfprintf(sp, " %s", *a); + if (!(s = costash(sp))) + goto nospace; + errormsg(state.lib, ERROR_LIBRARY|2, "service %s:%s", cs->path, s); + } + if (pipe(fds) < 0) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "%s: cannot allocate service pipe", cs->name); + return 0; + } + if (co->flags & CO_SHELL) + for (i = 0; i < elementsof(fds); i++) + if (fds[i] < 10 && (j = fcntl(fds[i], F_DUPFD, 10)) >= 0) + { + close(fds[i]); + fds[i] = j; + } + cs->fd = fds[1]; + ops[0] = PROC_FD_DUP(fds[0], 0, PROC_FD_PARENT); + ops[1] = PROC_FD_CLOSE(fds[1], PROC_FD_CHILD); + ops[2] = PROC_FD_DUP(co->gsmfd, 1, 0); + ops[3] = 0; + if (!(proc = procopen(cs->path, cs->argv, NiL, ops, PROC_DAEMON|PROC_IGNORE))) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "%s: cannot connect to %s service", cs->path, cs->name); + close(fds[0]); + close(fds[1]); + return 0; + } + fcntl(cs->fd, F_SETFD, FD_CLOEXEC); + cs->pid = proc->pid; + procfree(proc); + sfprintf(sp, "id=%d info\n", cj->id); + n = sfstrtell(sp); + if (!(s = costash(sp))) + goto bad; + if (write(cs->fd, s, n) != n || sfpoll(&co->msgfp, 1, 5 * 1000) <= 0) + goto bad; + cj->pid = 0; + cj->status = 0; + cj->local = 0; + cj->service = cs; + co->svc_outstanding++; + co->svc_running++; + if (!cowait(co, cj, -1)) + goto bad; + return cj; + bad: + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "%s: service not responding", cs->name); + nospace: + cj->pid = CO_PID_FREE; + cs->pid = 0; + close(cs->fd); + cs->fd = -1; + return 0; +} + +static Cojob_t* +request(register Coshell_t* co, Cojob_t* cj, Coservice_t* cs, const char* action, int flags) +{ + ssize_t n; + ssize_t i; + Sfio_t* sp; + + if (!(sp = sfstropen())) + { + errormsg(state.lib, ERROR_LIBRARY|2, "out of space"); + return 0; + } + if (!cs->fd && !service(co, cs, cj, flags, sp)) + goto bad; + if (!cs->pid) + goto bad; + if (flags & CO_DEBUG) + errormsg(state.lib, ERROR_LIBRARY|2, "job %d commands:\n\n%s %s\n", cj->id, cs->name, action); + if (!(flags & CO_SILENT)) + sfprintf(sfstderr, "+ %s %s\n", cs->name, action); + sfprintf(sp, "id=%d %s\n", cj->id, action); + n = sfstrtell(sp); + action = sfstrbase(sp); + while ((i = write(cs->fd, action, n)) > 0 && (n -= i) > 0) + action += i; + sfstrclose(sp); + if (n) + goto bad; + sfclose(sp); + cj->pid = 0; + cj->status = 0; + cj->local = 0; + cj->service = cs; + co->svc_outstanding++; + co->svc_running++; + co->total++; + return cj; + bad: + cj->pid = CO_PID_FREE; + sfclose(sp); + return 0; +} + +Cojob_t* +coexec(register Coshell_t* co, const char* action, int flags, const char* out, const char* err, const char* att) +{ + register Cojob_t* cj; + register Sfio_t* sp; + register Coservice_t* cs; + int n; + int i; + int og; + int cg; + char* s; + char* t; + char* env; + char* red; + char* sh[4]; + struct stat sto; + struct stat ste; + + /* + * get a free job slot + */ + + for (cj = co->jobs; cj; cj = cj->next) + if (cj->pid == CO_PID_FREE) + break; + if (cj) + cj->service = 0; + else if (!(cj = vmnewof(co->vm, 0, Cojob_t, 1, 0))) + return 0; + else + { + cj->coshell = co; + cj->pid = CO_PID_FREE; + cj->id = ++co->slots; + cj->next = co->jobs; + co->jobs = cj; + } + + /* + * set the flags + */ + + flags &= ~co->mask; + flags |= co->flags; + cj->flags = flags; + + /* + * check service intercepts + */ + + for (cs = co->service; cs; cs = cs->next) + { + for (s = cs->name, t = (char*)action; *s && *s == *t; s++, t++); + if (!*s && *t == ' ') + return request(co, cj, cs, t + 1, flags); + } + cj->flags &= ~CO_SERVICE; + red = (cj->flags & CO_APPEND) ? ">>" : ">"; + + /* + * package the action + */ + + if (!(env = coinitialize(co, co->flags))) + return 0; + if (!(sp = sfstropen())) + return 0; + n = strlen(action); + if (co->flags & CO_SERVER) + { + /* + * leave it to server + */ + + sfprintf(sp, "#%05d\ne %d %d %s %s %s", + 0, + cj->id, + cj->flags, + state.pwd, + out, + err); + if (att) + sfprintf(sp, " (%d:%s)", strlen(att), att); + else + sfprintf(sp, " %s", att); + sfprintf(sp, " (%d:%s) (%d:%s)\n", strlen(env), env, n, action); + } + else if (co->flags & CO_INIT) + { + if (flags & CO_DEBUG) + sfprintf(sp, "set -x\n"); + sfprintf(sp, "%s%s\necho x %d $? >&$%s\n", + env, + action, + cj->id, + CO_ENV_MSGFD); + } + else if (flags & CO_KSH) + { +#if !_lib_fork && defined(_map_spawnve) + Sfio_t* tp; + + tp = sp; + if (!(sp = sfstropen())) + sp = tp; +#endif + sfprintf(sp, "{\ntrap 'set %s$?; trap \"\" 0; IFS=\"\n\"; print -u$%s x %d $1 $(times); exit $1' 0 HUP INT QUIT TERM%s\n%s%s%s", + (flags & CO_SILENT) ? "" : "+x ", + CO_ENV_MSGFD, + cj->id, + (flags & CO_IGNORE) ? "" : " ERR", + env, + n > CO_MAXEVAL ? "" : "eval '", + (flags & CO_SILENT) ? "" : "set -x\n"); + if (n > CO_MAXEVAL) + sfputr(sp, action, -1); + else + { + coquote(sp, action, 0); + sfprintf(sp, "\n'"); + } + sfprintf(sp, "\n} out = pathtemp(NiL, 64, NiL, "coo", NiL))) + sfprintf(sp, " >%s", cj->out); + if (err) + { + if (out && streq(out, err)) + sfprintf(sp, " 2>&1"); + else if (*err == '/') + sfprintf(sp, " 2%s%s", red, err); + else + sfprintf(sp, " 2%s%s/%s", red, state.pwd, err); + } + else if (flags & CO_SERIALIZE) + { + if (!out && !fstat(1, &sto) && !fstat(2, &ste) && sto.st_dev == ste.st_dev && sto.st_ino == ste.st_ino) + sfprintf(sp, " 2>&1"); + else if (cj->err = pathtemp(NiL, 64, NiL, "coe", NiL)) + sfprintf(sp, " 2>%s", cj->err); + } +#if !_lib_fork && defined(_map_spawnve) + if (sp != tp) + { + sfprintf(tp, "%s -c '", state.sh); + if (!(s = costash(sp))) + return 0; + coquote(tp, s, 0); + sfprintf(tp, "'"); + sfstrclose(sp); + sp = tp; + } +#endif + sfprintf(sp, " &\nprint -u$%s j %d $!\n", + CO_ENV_MSGFD, + cj->id); + } + else + { +#if !_lib_fork && defined(_map_spawnve) + Sfio_t* tp; + + tp = sp; + if (!(sp = sfstropen())) sp = tp; +#endif + flags |= CO_IGNORE; + if (co->mode & CO_MODE_SEPARATE) + { + flags &= ~CO_SERIALIZE; + og = '{'; + cg = '}'; + } + else + { + og = '('; + cg = ')'; + } + sfprintf(sp, "%c\n%s%sset -%s%s\n", + og, + env, + n > CO_MAXEVAL ? "" : "eval '", + (flags & CO_IGNORE) ? "" : "e", + (flags & CO_SILENT) ? "" : "x"); + if (n > CO_MAXEVAL) + sfprintf(sp, "%s", action); + else + { + coquote(sp, action, 0); + sfprintf(sp, "\n'"); + } + sfprintf(sp, "\n%c out = pathtemp(NiL, 64, NiL, "coo", NiL))) + sfprintf(sp, " >%s", cj->out); + if (err) + { + if (out && streq(out, err)) + sfprintf(sp, " 2>&1"); + else if (*err == '/') + sfprintf(sp, " 2%s%s", red, err); + else + sfprintf(sp, " 2%s%s/%s", red, state.pwd, err); + } + else if (flags & CO_SERIALIZE) + { + if (out) + sfprintf(sp, " 2>&1"); + else if (cj->err = pathtemp(NiL, 64, NiL, "coe", NiL)) + sfprintf(sp, " 2>%s", cj->err); + } + if (!(co->mode & CO_MODE_SEPARATE)) + { + if (flags & CO_OSH) + sfprintf(sp, " && echo x %d 0 >&$%s || echo x %d $? >&$%s", + cj->id, + CO_ENV_MSGFD, + cj->id, + CO_ENV_MSGFD); + else + sfprintf(sp, " && echo x %d 0 `times` >&$%s || echo x %d $? `times` >&$%s", + cj->id, + CO_ENV_MSGFD, + cj->id, + CO_ENV_MSGFD); + } +#if !_lib_fork && defined(_map_spawnve) + if (sp != tp) + { + sfprintf(tp, "%s -c '", state.sh); + if (!(s = costash(sp))) + return 0; + coquote(tp, s, 0); + sfprintf(tp, "'"); + sfstrclose(sp); + sp = tp; + } +#endif + if (!(co->mode & CO_MODE_SEPARATE)) + sfprintf(sp, " &\necho j %d $! >&$%s\n", + cj->id, + CO_ENV_MSGFD); + } + n = sfstrtell(sp); + if (!costash(sp)) + return 0; + if (flags & CO_SERVER) + sfprintf(sp, "#%05d\n", n - 7); + s = sfstrseek(sp, 0, SEEK_SET); + if (flags & CO_DEBUG) + errormsg(state.lib, ERROR_LIBRARY|2, "job %d commands:\n\n%s\n", cj->id, s); + if (co->mode & CO_MODE_SEPARATE) + { + sh[0] = state.sh; + sh[1] = "-c"; + sh[2] = s; + sh[3] = 0; + cj->status = procrun(state.sh, sh, 0); + sfstrclose(sp); + cj->pid = CO_PID_ZOMBIE; + cj->local = 0; + co->outstanding++; + co->total++; + } + else + { + /* + * send it off + */ + + while ((i = write(co->cmdfd, s, n)) > 0 && (n -= i) > 0) + s += i; + sfstrclose(sp); + if (n) + return 0; + + /* + * it's a job + */ + + cj->pid = 0; + cj->status = 0; + cj->local = 0; + co->outstanding++; + co->running++; + co->total++; + if (co->mode & CO_MODE_ACK) + cj = cowait(co, cj, -1); + } + return cj; +} diff --git a/src/lib/libcoshell/coexport.c b/src/lib/libcoshell/coexport.c new file mode 100644 index 0000000..4ba62a6 --- /dev/null +++ b/src/lib/libcoshell/coexport.c @@ -0,0 +1,80 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * at&t Research + * + * coshell export var set/unset + */ + +#include "colib.h" + +/* + * set or unset coshell export variable + */ + +int +coexport(Coshell_t* co, const char* name, const char* value) +{ + Coexport_t* ex; + char* v; + + if (!co->export) + { + if (!(co->exdisc = vmnewof(co->vm, 0, Dtdisc_t, 1, 0))) + return -1; + co->exdisc->link = offsetof(Coexport_t, link); + co->exdisc->key = offsetof(Coexport_t, name); + co->exdisc->size = 0; + if (!(co->export = dtnew(co->vm, co->exdisc, Dtset))) + { + vmfree(co->vm, co->exdisc); + return -1; + } + } + if (!(ex = (Coexport_t*)dtmatch(co->export, name))) + { + if (!value) + return 0; + if (!(ex = vmnewof(co->vm, 0, Coexport_t, 1, strlen(name)))) + return -1; + strcpy(ex->name, name); + dtinsert(co->export, ex); + } + if (ex->value) + { + vmfree(co->vm, ex->value); + ex->value = 0; + } + if (value) + { + if (!(v = vmstrdup(co->vm, value))) + return -1; + ex->value = v; + } + else + { + dtdelete(co->export, ex); + vmfree(co->vm, ex); + } + co->init.sync = 1; + return 0; +} diff --git a/src/lib/libcoshell/coinit.c b/src/lib/libcoshell/coinit.c new file mode 100644 index 0000000..d36fbde --- /dev/null +++ b/src/lib/libcoshell/coinit.c @@ -0,0 +1,431 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * return job initialization commands + */ + +#if _WIN32 +#undef _BLD_DLL +#define _BLD_DLL 1 +#endif + +#include "colib.h" + +#include +#include +#include + +static void +exid(Sfio_t* sp, const char* pre, const char* name, const char* pos) +{ + int c; + + sfputr(sp, pre, -1); + if ((c = *name++) && c != '=') + { + if (isdigit(c)) + sfputc(sp, '_'); + do + { + if (!isalnum(c)) + c = '_'; + sfputc(sp, c); + } while ((c = *name++) && c != '='); + } + else + sfputc(sp, '_'); + sfputr(sp, pos, -1); +} + +/* + * add n to the export list + * old!=0 formats in old style + * coex!=0 for CO_ENV_EXPORT + * if n prefixed by % then coquote conversion enabled + */ + +static void +putexport(Coshell_t* co, Sfio_t* sp, char* n, int old, int coex, int flags) +{ + int cvt; + char* v; + Coexport_t* ex; + + if (cvt = *n == '%') + n++; + + /* + * currently limited to valid identifer env var names + */ + + if (!co->export || !dtmatch(co->export, n)) + { + if (old) + cvt = 0; + if ((v = getenv(n)) && *v || coex && ((flags & CO_EXPORT) || co->export && dtsize(co->export) > 0)) + { + if (!old) + sfprintf(sp, "\\\n"); + exid(sp, " ", n, "='"); + if (coex && (flags & CO_EXPORT)) + v = "(*)"; + if (v) + coquote(sp, v, cvt); + if (coex && !(flags & CO_EXPORT)) + { + v = v ? ":" : ""; + for (ex = (Coexport_t*)dtfirst(co->export); ex; ex = (Coexport_t*)dtnext(co->export, ex)) + { + sfprintf(sp, "%s%s", v, ex->name); + exid(sp, v, ex->name, ""); + v = ":"; + } + } + sfputc(sp, '\''); + if (old) + exid(sp, "\nexport ", n, "\n"); + } + } +} + +/* + * return job initialization commands + */ + +char* +coinitialize(Coshell_t* co, int flags) +{ + register char* s; + int n; + int m; + int old; + int sync; + char* t; + long p; + Coexport_t* ex; + Sfio_t* sp; + Sfio_t* tp; + struct stat st; + + sync = co->init.sync; + co->init.sync = 0; + + /* + * pwd + */ + + if (stat(".", &st)) + return 0; + if (!state.pwd || st.st_ino != co->init.pwd_ino || st.st_dev != co->init.pwd_dev) + { + co->init.pwd_dev = st.st_dev; + co->init.pwd_ino = st.st_ino; + if (state.pwd) + free(state.pwd); + if (!(state.pwd = getcwd(NiL, 0))) + { + if (errno != EINVAL || !(state.pwd = newof(0, char, PATH_MAX, 0))) + return 0; + if (!getcwd(state.pwd, PATH_MAX)) + { + free(state.pwd); + state.pwd = 0; + return 0; + } + } + if (!(flags & CO_INIT)) + sync = 1; + } + + /* + * umask + */ + + umask(n = umask(co->init.mask)); + if (co->init.mask != n) + { + co->init.mask = n; + if (!(flags & CO_INIT)) + sync = 1; + } + if (!co->init.script || sync) + { + /* + * co_export[] vars + */ + + if (!(sp = sfstropen())) + return 0; + tp = 0; + old = !(flags & (CO_KSH|CO_SERVER)); + if (!old) + sfprintf(sp, "export"); + if (sync) + { + if (flags & CO_EXPORT) + s = "(*)"; + else + { + for (n = 0; s = co_export[n]; n++) + putexport(co, sp, s, old, !n, flags); + s = getenv(co_export[0]); + } + if (s) + { + if (*s == '(') + { + register char** ep = environ; + register char* e; + char* v; + char* es; + char* xs; + + if (v = strchr(s, ':')) + *v = 0; + while (e = *ep++) + if ((t = strsubmatch(e, s, 1)) && (*t == '=' || !*t && (t = strchr(e, '=')))) + { + m = (int)(t - e); + if (!strneq(e, "PATH=", 5) && !strneq(e, "_=", 2)) + { + for (n = 0; xs = co_export[n]; n++) + { + es = e; + while (*xs && *es == *xs) + { + es++; + xs++; + } + if (*es == '=' && !*xs) + break; + } + if (!xs) + { + if (!old) + sfprintf(sp, "\\\n"); + exid(sp, " ", e, "='"); + coquote(sp, e + m + 1, 0); + sfputc(sp, '\''); + if (old) + exid(sp, "\nexport ", e, "\n"); + } + } + } + if (v) + { + *v++ = ':'; + s = v; + } + } + if (*s) + for (;;) + { + if (t = strchr(s, ':')) + *t = 0; + putexport(co, sp, s, old, 0, 0); + if (!(s = t)) + break; + *s++ = ':'; + } + } + if (co->export) + for (ex = (Coexport_t*)dtfirst(co->export); ex; ex = (Coexport_t*)dtnext(co->export, ex)) + { + if (!old) + sfprintf(sp, "\\\n"); + exid(sp, " ", ex->name, "='"); + coquote(sp, ex->value, 0); + sfputc(sp, '\''); + if (old) + exid(sp, "\nexport ", ex->name, "\n"); + } + } + + /* + * PATH + */ + + if (!old) + sfprintf(sp, "\\\n"); + sfprintf(sp, " PATH='"); + n = PATH_MAX; + if (!(t = sfstrrsrv(sp, n))) + { + bad: + sfstrclose(sp); + if (tp) + sfstrclose(tp); + return 0; + } + t += n / 2; + if (!(flags & CO_CROSS) && !pathpath("ignore", NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, t, n / 2) && pathpath("bin/ignore", "", PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, t, n / 2)) + { + *strrchr(t, '/') = 0; + sfputc(sp, ':'); + coquote(sp, t, !old); + sfputc(sp, ':'); + s = pathbin(); + } + else + { + s = pathbin(); + if (!(flags & CO_CROSS)) + { + if (!sync && (*s == ':' || *s == '.' && *(s + 1) == ':')) + { + sfstrseek(sp, 0, SEEK_SET); + goto done; + } + sfputc(sp, ':'); + } + } + for (;;) + { + if (*s == ':') + s++; + else if (*s == '.' && *(s + 1) == ':') + s += 2; + else + break; + } + if (!(flags & CO_CROSS)) + tp = 0; + else if (!(tp = sfstropen())) + goto bad; + else + { + while (n = *s++) + { + if (n == ':') + { + while (*s == ':') + s++; + if (!*s) + break; + if (*s == '.') + { + if (!*(s + 1)) + break; + if (*(s + 1) == ':') + { + s++; + continue; + } + } + } + sfputc(tp, n); + } + if (!(s = costash(tp))) + goto bad; + } + coquote(sp, s, !old); + if (tp) + sfstrclose(tp); + sfputc(sp, '\''); + if (old) + sfprintf(sp, "\nexport PATH"); + sfputc(sp, '\n'); + if (sync) + { + /* + * VPATH + */ + + p = (int)sfstrtell(sp); + sfprintf(sp, "vpath "); + n = PATH_MAX; + if (fs3d(FS3D_TEST)) + for (;;) + { + if (!(t = sfstrrsrv(sp, n))) + goto bad; + if ((m = mount(NiL, t, FS3D_GET|FS3D_ALL|FS3D_SIZE(n), NiL)) > 0) + m = n; + else + { + if (!m) + sfstrseek(sp, strlen(t), SEEK_CUR); + break; + } + } + else + { + m = 0; + sfprintf(sp, "- /#option/2d"); + } + if (m) + sfstrseek(sp, p, SEEK_SET); + else + sfprintf(sp, " 2>/dev/null || :\n"); + sfprintf(sp, "umask 0%o\ncd '%s'\n", co->init.mask, state.pwd); + } + done: + if (!(flags & CO_SERVER)) + { + sfprintf(sp, "%s%s=%05d${!%s-$$}\n", old ? "" : "export ", CO_ENV_TEMP, getpid(), (flags & CO_OSH) ? "" : ":"); + if (old) + sfprintf(sp, "export %s\n", CO_ENV_TEMP); + } + sfputc(sp, 0); + n = (int)sfstrtell(sp); + if (co->vm) + { + if (co->init.script) + vmfree(co->vm, co->init.script); + if (!(co->init.script = vmnewof(co->vm, 0, char, n, 1))) + goto bad; + } + else + { + if (co->init.script) + free(co->init.script); + if (!(co->init.script = newof(0, char, n, 1))) + goto bad; + } + memcpy(co->init.script, sfstrbase(sp), n); + sfstrclose(sp); + } + else if (!co->init.script) + { + if (co->init.script = co->vm ? vmnewof(co->vm, 0, char, 1, 0) : newof(0, char, 1, 0)) + *co->init.script = 0; + } + return co->init.script; +} + +/* + * return generic job initialization commands + */ + +char* +coinit(int flags) +{ + if (!state.generic) + { + if (!(state.generic = newof(0, Coshell_t, 1, 0))) + return 0; + state.generic->init.sync = 1; + } + return coinitialize(state.generic, flags); +} diff --git a/src/lib/libcoshell/cokill.c b/src/lib/libcoshell/cokill.c new file mode 100644 index 0000000..b0c4fd4 --- /dev/null +++ b/src/lib/libcoshell/cokill.c @@ -0,0 +1,134 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * if co==0 then kill all coshell jobs with sig + * elif cj==0 then kill co jobs with sig + * else kill cj with sig + * + * if sig==0 then cause all CO_SERVICE jobs to fail + */ + +#include "colib.h" + +/* + * kill job cj in shell co with signal sig + */ + +static int +cokilljob(register Coshell_t* co, register Cojob_t* cj, int sig) +{ + int n; + + if (co->flags & CO_DEBUG) + errormsg(state.lib, 2, "coshell %d kill co=%d cj=%d sig=%d", co->index, co->pid, cj->pid, sig); + if (cj->pid < 0) + return 0; + if (cj->pid == 0) + { + if (cj->service) + co->svc_running--; + else + co->running--; + cj->pid = CO_PID_ZOMBIE; + cj->status = EXIT_TERM(sig); + return 0; + } + if (sig == SIGKILL) + { + co->running--; + cj->pid = CO_PID_ZOMBIE; + cj->status = EXIT_TERM(sig); + } + n = kill(cj->pid, sig); + killpg(cj->pid, sig); + return n; +} + +/* + * kill cj (or all jobs if cj==0) in shell co with sig + */ + +static int +cokillshell(register Coshell_t* co, register Cojob_t* cj, int sig) +{ + int n; + + if (sig && (co->flags & CO_SERVER)) + { + char buf[CO_BUFSIZ]; + + n = sfsprintf(buf, sizeof(buf), "#%05d\nk %d %d\n", 0, cj ? cj->id : 0, sig); + sfsprintf(buf, 7, "#%05d\n", n - 7); + return write(co->cmdfd, buf, n) == n ? 0 : -1; + } + if (cj) + return cokilljob(co, cj, sig); + n = 0; + for (cj = co->jobs; cj; cj = cj->next) + if (cj->pid > 0) + n |= cokilljob(co, cj, sig); + return n; +} + +int +cokill(register Coshell_t* co, register Cojob_t* cj, int sig) +{ + int any; + int n; + + if (cj) + { + if (!co) + co = cj->coshell; + else if (co != cj->coshell) + return -1; + any = 0; + } + else if (co) + any = 0; + else if (!(co = state.coshells)) + return -1; + else + any = 1; + if (co->flags & CO_DEBUG) + errormsg(state.lib, 2, "coshell %d kill co=%d cj=%d sig=%d", co->index, co ? co->pid : 0, cj ? cj->pid : 0, sig); + switch (sig) + { + case SIGINT: + sig = SIGTERM; + break; +#if defined(SIGSTOP) && defined(SIGTSTP) + case SIGTSTP: + sig = SIGSTOP; + break; +#endif + } + n = 0; + do + { + cowait(co, (Cojob_t*)co, 0); + n |= cokillshell(co, cj, sig); + } while (any && (co = co->next)); + return n; +} diff --git a/src/lib/libcoshell/colib.h b/src/lib/libcoshell/colib.h new file mode 100644 index 0000000..e4512b5 --- /dev/null +++ b/src/lib/libcoshell/colib.h @@ -0,0 +1,137 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * coshell library private definitions + */ + +#ifndef _COLIB_H +#define _COLIB_H + +#include +#include +#include + +#define _CO_JOB_PRIVATE_ /* Cojob_t private additions */ \ + Cojob_t* next; /* next in list */ \ + Coservice_t* service; /* service */ \ + int pid; /* pid */ \ + char* out; /* serialized stdout file */ \ + char* err; /* serialized stderr file */ \ + /* end of private additions */ + +#define _CO_SHELL_PRIVATE_ /* Coshell_t private additions */ \ + Vmalloc_t* vm; /* Coshell_t vm */ \ + Coshell_t* next; /* next in list */ \ + Cojob_t* jobs; /* job list */ \ + Coservice_t* service; /* service */ \ + Dt_t* export; /* coexport() dictionary */ \ + Dtdisc_t* exdisc; /* coexport() discipline */ \ + struct Coinit_s /* initialization script state */ \ + { \ + char* script; /* initialization script */ \ + dev_t pwd_dev; /* previous pwd dev */ \ + ino_t pwd_ino; /* previous pwd inode number */ \ + int mask; /* previous umask */ \ + int sync; /* sync script */ \ + } init; \ + int cmdfd; /* command pipe fd */ \ + int gsmfd; /* msgfp child write side */ \ + int mask; /* CO_* flags to clear */ \ + int mode; /* connection modes */ \ + int svc_outstanding;/* outstanding service intercepts */ \ + int svc_running; /* running service intercepts */ \ + int pid; /* pid */ \ + int index; /* coshell index */ \ + int slots; /* number of job slots */ \ + /* end of private additions */ + +typedef struct Coexport_s +{ + Dtlink_t link; + char* value; + char name[1]; +} Coexport_t; + +struct Coservice_s; +typedef struct Coservice_s Coservice_t; + +struct Coservice_s /* service info */ +{ + Coservice_t* next; /* next in list */ + char* name; /* instance name */ + char* path; /* coexec() command path */ + char* db; /* state/db path */ + int fd; /* command pipe */ + int pid; /* pid */ + char* argv[16]; /* coexec() command argv[] */ +}; + +#include +#include +#include +#include + +#define state _coshell_info_ /* hide external symbol */ + +#define CO_MODE_ACK (1<<0) /* wait for coexec() ack */ +#define CO_MODE_INDIRECT (1<<1) /* indirect CO_SERVER */ +#define CO_MODE_SEPARATE (1<<2) /* 1 shell+wait per action */ + +#define CO_INIT (CO_USER>>1) /* initial command */ + +#define CO_PID_FREE (-3) /* free job slot */ +#define CO_PID_WARPED (-2) /* exit before start message */ +#define CO_PID_ZOMBIE (-1) /* ready for wait */ + +#define CO_BUFSIZ (PATH_MAX/2) /* temporary buffer size */ +#define CO_MAXEVAL (PATH_MAX*8) /* max eval'd action size */ + +typedef struct Costate_s /* global coshell state */ +{ + const char* lib; /* library id */ + Coshell_t* coshells; /* list of all coshells */ + Coshell_t* current; /* current coshell */ + Coshell_t* generic; /* generic coshell for coinit() */ + char* pwd; /* pwd */ + char* sh; /* sh from first coopen() */ + char* type; /* CO_ENV_TYPE value */ + int init; /* 0 if first coopen() */ + int index; /* last coshell index */ +} Costate_t; + +extern char coident[]; /* coshell ident script */ +extern char cobinit[]; /* bsh initialition script */ +extern char cokinit[]; /* ksh initialition script */ +extern char* co_export[]; /* default export var list */ + +extern Costate_t state; /* global coshell info */ + +#ifndef errno +extern int errno; +#endif + +extern char* costash(Sfio_t*); +extern char* coinitialize(Coshell_t*, int); + +#endif diff --git a/src/lib/libcoshell/coopen.c b/src/lib/libcoshell/coopen.c new file mode 100644 index 0000000..07dcb87 --- /dev/null +++ b/src/lib/libcoshell/coopen.c @@ -0,0 +1,411 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * open a new coshell + */ + +#include "colib.h" + +#include +#include +#include +#include + +static const Namval_t options[] = +{ + "cross", CO_CROSS, + "debug", CO_DEBUG, + "devfd", CO_DEVFD, + "ignore", CO_IGNORE, + "orphan", CO_ORPHAN, + "silent", CO_SILENT, + "separate", CO_SEPARATE, + "service", CO_SERVICE, + 0, 0 +}; + +Costate_t state = { "libcoshell:coshell" }; + +/* + * called when ident sequence hung + */ + +static void +hung(int sig) +{ + NoP(sig); + close(sffileno(state.current->msgfp)); +} + +/* + * close all open coshells + */ + +static void +clean(void) +{ + coclose(NiL); +} + +#ifdef SIGCONT + +/* + * pass job control signals to the coshell and self + */ + +static void +stop(int sig) +{ + cokill(NiL, NiL, sig); + signal(sig, SIG_DFL); + sigunblock(sig); + kill(getpid(), sig); + cokill(NiL, NiL, SIGCONT); + signal(sig, stop); +} + +#endif + +/* + * called by stropt() to set options + */ + +static int +setopt(void* handle, register const void* p, int n, const char* v) +{ + Coshell_t* co = (Coshell_t*)handle; + Coservice_t* cs; + char* s; + char** a; + + NoP(v); + if (p) + { + if (n) + { + co->flags |= ((Namval_t*)p)->value; + if (((Namval_t*)p)->value == CO_SERVICE && v && (cs = vmnewof(co->vm, 0, Coservice_t, 1, 2 * strlen(v)))) + { + a = cs->argv; + *a++ = s = cs->path = cs->name = (char*)(cs + 1); + while (*s = *v++) + if (*s++ == ':') + { + *(s - 1) = 0; + if (*v == '-') + { + v++; + if (*v == '-') + v++; + } + if (strneq(v, "command=", 8)) + cs->path = s + 8; + else if (strneq(v, "state=", 6)) + cs->db = s + 6; + else if (strneq(v, "db=", 3)) + cs->db = s + 3; + else if (a < &cs->argv[elementsof(cs->argv)-2] && *v && *v != ':') + { + *a++ = s; + *s++ = '-'; + *s++ = '-'; + } + } + if (cs->db) + *a++ = cs->db; + *a = 0; + cs->next = co->service; + co->service = cs; + } + } + else + co->mask |= ((Namval_t*)p)->value; + } + return 0; +} + +Coshell_t* +coopen(const char* path, int flags, const char* attributes) +{ + register Coshell_t* co; + register char* s; + register int i; + char* t; + int n; + Proc_t* proc; + Cojob_t* cj; + Vmalloc_t* vm; + Sfio_t* sp; + Sig_handler_t handler; + int pio[4]; + long ops[5]; + char devfd[16]; + char evbuf[sizeof(CO_ENV_MSGFD) + 8]; + char* av[8]; + char* ev[2]; + + static char* sh[] = { 0, 0, "ksh", "sh", "/bin/sh" }; + + if (!state.type && (!(s = getenv(CO_ENV_TYPE)) || !(state.type = strdup(s)))) + state.type = ""; + if ((flags & CO_ANY) && (co = state.coshells)) + return co; + if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(co = vmnewof(vm, 0, Coshell_t, 1, 0))) + { + if (vm) + vmclose(vm); + errormsg(state.lib, ERROR_LIBRARY|2, "out of space"); + return 0; + } + co->vm = vm; + co->index = ++state.index; + stropt(getenv(CO_ENV_OPTIONS), options, sizeof(*options), setopt, co); + if (attributes) + stropt(attributes, options, sizeof(*options), setopt, co); + co->flags |= ((flags | CO_DEVFD) & ~co->mask); + if (co->flags & CO_SEPARATE) + { + co->flags &= ~CO_SEPARATE; + co->mode |= CO_MODE_SEPARATE; + } + co->flags |= CO_INIT; + if (co->mode & CO_MODE_SEPARATE) + { + flags = 0; + proc = 0; + } + else + { + for (i = 0; i < elementsof(pio); i++) + pio[i] = -1; + if (pipe(&pio[0]) < 0 || pipe(&pio[2]) < 0) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot allocate pipes"); + goto bad; + } + if (flags & CO_SHELL) + for (i = 0; i < elementsof(pio); i++) + if (pio[i] < 10 && (n = fcntl(pio[i], F_DUPFD, 10)) >= 0) + { + close(pio[i]); + pio[i] = n; + } + co->cmdfd = pio[1]; + co->gsmfd = pio[3]; + if (!(co->msgfp = sfnew(NiL, NiL, 256, pio[2], SF_READ))) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot allocate message stream"); + goto bad; + } + sfdcslow(co->msgfp); + ops[0] = PROC_FD_DUP(pio[0], 0, PROC_FD_PARENT); + ops[1] = PROC_FD_CLOSE(pio[1], PROC_FD_CHILD); + ops[2] = PROC_FD_CLOSE(pio[2], PROC_FD_CHILD); + ops[3] = PROC_FD_CLOSE(pio[3], PROC_FD_PARENT); + ops[4] = 0; + sfsprintf(devfd, sizeof(devfd), "/dev/fd/%d", pio[0]); + flags = !access(devfd, F_OK); + } + sh[0] = (char*)path; + sh[1] = getenv(CO_ENV_SHELL); + for (i = 0; i < elementsof(sh); i++) + if ((s = sh[i]) && *s && (s = strdup(s))) + { + if ((n = tokscan(s, NiL, " %v ", av, elementsof(av) - 1)) > 0) + { + if (t = strrchr(s = av[0], '/')) + av[0] = t + 1; + if (flags || (co->flags & CO_DEVFD) && strmatch(s, "*ksh*")) + av[n++] = devfd; + av[n] = 0; + sfsprintf(evbuf, sizeof(evbuf), "%s=%d", CO_ENV_MSGFD, co->gsmfd); + ev[0] = evbuf; + ev[1] = 0; + if ((co->mode & CO_MODE_SEPARATE) || (proc = procopen(s, av, ev, ops, (co->flags & (CO_SHELL|CO_ORPHAN)) ? (PROC_ORPHAN|PROC_DAEMON|PROC_IGNORE) : (PROC_DAEMON|PROC_IGNORE)))) + { + if (!state.sh) + state.sh = strdup(s); + free(s); + if (proc) + { + co->pid = proc->pid; + procfree(proc); + } + break; + } + } + free(s); + } + if (i >= elementsof(sh)) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot execute"); + goto bad; + } + if (!(co->mode & CO_MODE_SEPARATE)) + { + /* + * send the shell identification sequence + */ + + if (!(sp = sfstropen())) + { + errormsg(state.lib, ERROR_LIBRARY|2, "out of buffer space"); + goto bad; + } + sfprintf(sp, "#%05d\n%s='", 0, CO_ENV_ATTRIBUTES); + if (t = getenv(CO_ENV_ATTRIBUTES)) + { + coquote(sp, t, 0); + if (attributes) + sfprintf(sp, ","); + } + if (attributes) + coquote(sp, attributes, 0); + sfprintf(sp, "'\n"); + sfprintf(sp, coident, CO_ENV_MSGFD, pio[3], CO_ENV_MSGFD, CO_ENV_MSGFD, CO_ENV_MSGFD); + i = sfstrtell(sp); + sfstrseek(sp, 0, SEEK_SET); + sfprintf(sp, "#%05d\n", i - 7); + i = write(co->cmdfd, sfstrbase(sp), i) != i; + sfstrclose(sp); + if (i) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot write initialization message"); + goto nope; + } + state.current = co; + handler = signal(SIGALRM, hung); + i = alarm(30); + if (!(s = sfgetr(co->msgfp, '\n', 1))) + { + if (errno == EINTR) + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "identification message read timeout"); + goto nope; + } + alarm(i); + signal(SIGALRM, handler); + if (co->flags & CO_DEBUG) + errormsg(state.lib, 2, "coshell %d shell path %s identification \"%s\"", co->index, state.sh, s); + switch (*s) + { + case 'o': + co->flags |= CO_OSH; + /*FALLTHROUGH*/ + case 'b': + s = cobinit; + break; + case 'k': + co->flags |= CO_KSH; + s = cokinit; + break; + case 'i': /* NOTE: 'i' is obsolete */ + case 's': + co->flags |= CO_SERVER; + co->pid = 0; + for (;;) + { + if (t = strchr(s, ',')) + *t = 0; + if (streq(s, CO_OPT_ACK)) + co->mode |= CO_MODE_ACK; + else if (streq(s, CO_OPT_INDIRECT)) + co->mode |= CO_MODE_INDIRECT; + if (!(s = t)) + break; + s++; + } + if (!(co->mode & CO_MODE_INDIRECT)) + wait(NiL); + break; + default: + goto nope; + } + if (s) + { + if (!(cj = coexec(co, s, 0, NiL, NiL, NiL)) || cowait(co, cj, -1) != cj) + { + errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "initialization message exec error"); + goto nope; + } + co->total = 0; + co->user = 0; + co->sys = 0; + } + } + co->flags &= ~CO_INIT; + fcntl(pio[1], F_SETFD, FD_CLOEXEC); + fcntl(pio[2], F_SETFD, FD_CLOEXEC); + co->next = state.coshells; + state.coshells = co; + if (!(co->flags & CO_SHELL)) + { +#ifdef SIGCONT +#ifdef SIGTSTP + signal(SIGTSTP, stop); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, stop); +#endif +#ifdef SIGTTOU + signal(SIGTTOU, stop); +#endif +#endif + if (!state.init) + { + state.init = 1; + atexit(clean); + } + } + return co; + bad: + n = errno; + if (co->msgfp) + { + sfclose(co->msgfp); + pio[2] = -1; + } + for (i = 0; i < elementsof(pio); i++) + if (pio[i] >= 0) + close(pio[i]); + coclose(co); + errno = n; + return 0; + nope: + i = errno; + if (!(s = sh[1]) || (s = (t = strrchr(s, '/')) ? (t + 1) : s) && !strmatch(s, "?(k)sh") && !streq(s, CO_ID)) + error(2, "export %s={ksh,sh,%s}", CO_ENV_SHELL, CO_ID); + coclose(co); + errno = i; + return 0; +} + +/* + * set coshell attributes + */ + +int +coattr(Coshell_t* co, const char* attributes) +{ + return 0; +} diff --git a/src/lib/libcoshell/coquote.c b/src/lib/libcoshell/coquote.c new file mode 100644 index 0000000..1c1edf2 --- /dev/null +++ b/src/lib/libcoshell/coquote.c @@ -0,0 +1,60 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * single quote s into sp + * if type!=0 then /)/ translated to /$/ + */ + +#include "colib.h" + +void +coquote(register Sfio_t* sp, register const char* s, int type) +{ + register int c; + + if (type && (!state.type || !*state.type)) + type = 0; + while (c = *s++) + { + sfputc(sp, c); + if (c == '\'') + { + sfputc(sp, '\\'); + sfputc(sp, '\''); + sfputc(sp, '\''); + } + else if (type && c == '/' && *s == *state.type) + { + register const char* x = s; + register char* t = state.type; + + while (*t && *t++ == *x) x++; + if (!*t && *x == '/') + { + s = x; + sfprintf(sp, "'$%s'", CO_ENV_TYPE); + } + } + } +} diff --git a/src/lib/libcoshell/coshell.3 b/src/lib/libcoshell/coshell.3 new file mode 100644 index 0000000..b0a1201 --- /dev/null +++ b/src/lib/libcoshell/coshell.3 @@ -0,0 +1,396 @@ +.fp 5 CW +.de L \" literal font +.ft 5 +.if !\\$1 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \f1 +.. +.de LR +.}S 5 1 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de RL +.}S 1 5 \& "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" +.. +.de EX \" start example +.ta 1i 2i 3i 4i 5i 6i +.PP +.RS +.PD 0 +.ft 5 +.nf +.. +.de EE \" end example +.fi +.ft +.PD +.RE +.PP +.. +.TH COSHELL 3 +.SH NAME \" @(#)coshell.3 (gsf@research.att.com) 10/11/90 +coshell \- shell coprocess support +.SH SYNOPSIS +.L "#include " +.br +.L "\-lcoshell \-last" +.SH DESCRIPTION +The +.I coshell +routines support the shell as a coprocess. +This coprocess may be either +.IR ksh (1) +or +.IR sh (1) +executing on the local host, or it may be +.IR coshell (1) +with access to shells on hosts throughout the local network. +.PP +The coshell inherits the environment of the calling process. +Signals sent to the calling process are passed to the coshell. +.PP +More than one coshell may be open in the current process. +If the +.L Coshell_t* +argument to the +.LR cowait() , +.LR cokill() , +.LR copending() , +.LR cozombie() , +or +.L coclose() +calls below is +.L 0 +then the call is applied to all open coshell. +.PP +.L "Coshell_t* coopen(const char* shell, int flags, const char* attributes)" +.PP +Returns a pointer to a new coshell. +.L NULL +is returned on error. +If +.L shell +is +.L NULL +then the coshell executable is determined by doing the usual path search, +in order, on the value of the environment variable +.B COSHELL +and the commands +.BR ksh +and +.BR sh . +.L flags +is the inclusive-or of the following: +.TP +.L CO_ANY +Return a pointer to a previously opened coshell if possible, otherwise +open a new coshell. +.TP +.L CO_DEBUG +Enable library debug tracing. +.TP +.L CO_IGNORE +Ignore any command errors. +By default any command error that is not tested by a condtional causes +the job to terminate. +.TP +.L CO_LOCAL +Commands are to be executed on the local host only. +.TP +.L CO_SHELL +The caller is +.BR sh (1): +internal file descriptors are moved to 10 or above; +SIGSTOP and SIGCONT handlers are not installed. +.TP +.L CO_SILENT +Don't trace commands. +By default commands are traced using the shell +.B \-x +option. +.TP +.L CO_NONBLOCK +Normally +.L coexec() +blocks when the job queue is full and waits until a job completes. +.L CO_NONBLOCK +causes +.L coexec() +to return +.L NULL +when the job queue is full. +.PP +.L attributes +is a string that is interpreted by the coshell. +If +.L attributes +is +.L NULL +then the value of the environment variable +.B COATTRIBUTES +is used if defined. +.B ksh +and +.B sh +ignore this string. +The return value points to a structure with the following readonly elements: +.TP +.L "int flags" +The default flags. +.TP +.L "int outstanding" +The number of jobs that have not been waited for. +.TP +.L "int running" +The number of jobs still running. +.TP +.L "int total" +The total number of jobs sent to the coshell. +.TP +.L "unsigned long user" +The total user time of all completed jobs in +.L 1/CO_QUANT +second increments. +.TP +.L "unsigned long sys" +The total system time of all completed jobs in +.L 1/CO_QUANT +second increments. +.PP +.L "int coclose(Coshell_t* sh)" +.PP +Close an open coshell pointed to by +.LR sh . +The coshell exit status is returned. +.PP +.L "Cojob_t* coexec(Coshell_t* sh, const char* cmd, int flags, const char* out, const char* err, const char* att)" +.PP +Sends the shell command line +.L cmd +to the open coshell pointed to by +.L sh +for execution. +.L flags +are the same as in the +.L coopen() +call, and are used to augment the default settings from +.LR coopen() . +.L out +is the standard output file name and defaults to +.B stdout +if +.LR NULL . +.L err +is the standard error file name and defaults to +.B stderr +if +.LR NULL . +.LR att , +if +.RL non- NULL , +contains job attributes that are appended to the attributes from +.L coopen() +before being sent to the coshell. +The return value points to a structure with the following elements: +.TP +.L "int id" +A number that uniquely identifies the job within the coshell. +.TP +.L "int status" +The job exit status, valid only for job pointers returned by +.LR cowait() . +.TP +.L "int flags" +The flags enabled for this job. +.TP +.L "void* local" +A user reserved pointer, initially set to +.L NULL +on return from +.LR coexec() . +This is the only job field that may be modified by the user. +The user defined value is preserved until after the +.L cowait() +call that returns the job pointer. +.TP +.L "unsigned long user" +The user time of this job in +.L 1/CO_QUANT +second increments, valid only for job pointers returned by +.LR cowait() . +.TP +.L "unsigned long sys" +The system time of this job in +.L 1/CO_QUANT +second increments, valid only for job pointers returned by +.LR cowait() . +.PP +.L "Cojob_t* cowait(Coshell_t* sh, Cojob_t* job, int timeout)" +.PP +Returns the job pointer in the coshell pointed to by +.L sh +for the job pointed to by +.LR job . +If +.L job +is +.L NULL +then a pointer to any completed job is returned. +.L cowait() +blocks until the specified job(s) complete. +.L NULL +is returned on error or if all jobs have completed and +.L errno +is set to EINVAL for coshell communication errors, +ECHILD if +.L job +is +.L NULL +and there are no children, and ESRCH if +.L job +is not +.L NULL +and not an active job. +.L "cozombie(sh)" +is the number of jobs that may be waited for without blocking. +The return value +.LR status , +.L user +and +.L sys +job fields are set to their final values. +The return value is valid until the next +.LR coexec() , +.L cowait() +or +.L coclose() +call. +.L timeout +is the maximum time in milliseconds that wait will block. +If the wait times out then 0 is returned. +A negative +.L timeout +waits until a job completes or a signal is received. +.TP +.L "int cojobs(Coshell_t* sh)" +Returns the number of outstanding jobs that are children of the caller. +(Remote jobs or jobs executed by a separate daemon are not counted here.) +.TP +.L "int copending(Coshell_t* sh)" +Returns the number of pending jobs; this is the number of +.L cowait() +calls required to reap all running jobs. +.TP +.L "int cozombie(Coshell_t* sh)" +Returns the number of jobs that have completed but have not been +.L cowait()'d +for. +.TP +.L "int cokill(Coshell_t* sh, Cojob_t* job, int sig)" +The signal +.L sig +is sent to the job pointed to by +.L job +running in the coshell pointed to by +.LR sh . +If +.L job +is +.L NULL +then the signal is sent to all jobs in the coshell. +If both +.L sh +and +.L job +are +.L NULL +then the signal is sent to all jobs in all coshells. +.L \-1 +is returned on error, +.L 0 +otherwise. +.TP +.L "int cosync(Coshell_t* sh, const char* path, int fd, int mode)" +Sync all outstanding file operations for either the file +.L path +or the file descriptor +.L fd +after its shell action has completed in +.LR sh . +If +.L path +is +.L NULL +then +.L fd +is used. +If +.L fd<0 +and +.L mode>=0 +then +.L path +is opened using +.L mode. +This is an unfortunate workaround for NFS and remote coshells, and is a +no-op for all real file systems. +It should be called after +.L cowait() +to ensure that all file system cache info has been flushed. +.IR sync (2) +or +.IR fsync (2) +must still be called to schedule file data to be written to disk. +.TP +.L "char* coinit(Coshell_t* sh)" +Returns the shell initialization commands for the next job. +These commands represent process state changes that may have occurred +since the last call to +.L coinit(), +e.g., current working directory or umask. +If +.L sh +is local (a child of the calling process) +.L coinit() +may return the empty string; +if +.L sh +is remote then considerably more information may be returned. +This routine is used by remote coshell implementations and is +not normally called from user code. +.TP +.L "void coquote(Sfio_T* sp, const char* string, int type)" +Applies shell single quoting to +.L string +and copies the result into the sfio stream +.L sp. +If +.L type!=0 +then any occurence of \f5/\fP\fIhosttype\fP\f5/\fP is translated to +\f5/$HOSTTYPE/\fP, where +.I hosttype +is the current value of the +.L HOSTTYPE +environment variable. +This routine is used by remote coshell implementations and is +not normally called from user code. +.SH CAVEATS +.L cosync() +is a hack workaround, but we do have to work in the real world. +.PP +A bug in +.IR bsh (1) +and +.IR ksh (1) +implementations up to and including ksh88e causes some interrupted +jobs to return 0 exit status. +This should be fixed in later shell releases. +.PP +.L "trap 0" +is reserved by +.L coexec() +at the outermost scope. +To use +.L "trap 0" +use +.L "(...)" +to force a subshell. +.SH "SEE ALSO" +coshell(1), ksh(1), nmake(1), sh(1), cs(3), libast(3) diff --git a/src/lib/libcoshell/coshell.h b/src/lib/libcoshell/coshell.h new file mode 100644 index 0000000..9063f14 --- /dev/null +++ b/src/lib/libcoshell/coshell.h @@ -0,0 +1,143 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * coshell library interface + */ + +#ifndef _COSHELL_H +#define _COSHELL_H + +#include + +#if !_BLD_coshell + +#undef procrun +#define procrun(a,b,c) coprocrun(a,b,c) +#undef system +#define system(a) cosystem(a) + +#endif + +struct Coshell_s; typedef struct Coshell_s Coshell_t; +struct Cojob_s; typedef struct Cojob_s Cojob_t; + +/* + * DEPRECATED names for compatibility + */ + +#define COSHELL Coshell_t +#define COJOB Cojob_t + +#define CO_ID "coshell" /* library/command id */ + +#define CO_ENV_ATTRIBUTES "COATTRIBUTES"/* coshell attributes env var */ +#define CO_ENV_EXPORT "COEXPORT" /* coshell env var export list */ +#define CO_ENV_HOST "HOSTNAME" /* coshell host name env var */ +#define CO_ENV_MSGFD "_COSHELL_msgfd"/* msg fd */ +#define CO_ENV_OPTIONS "COSHELL_OPTIONS"/* options environment var */ +#define CO_ENV_PROC "NPROC" /* concurrency environment var */ +#define CO_ENV_SHELL "COSHELL" /* coshell path environment var */ +#define CO_ENV_TEMP "COTEMP" /* 10 char temp file base */ +#define CO_ENV_TYPE "HOSTTYPE" /* coshell host type env var */ + +#define CO_OPT_ACK "ack" /* wait for server coexec() ack */ +#define CO_OPT_INDIRECT "indirect" /* indirect server connection */ +#define CO_OPT_SERVER "server" /* server connection */ + +#define CO_QUANT 100 /* time quanta per sec */ + +#define CO_ANY 0x000001 /* return any open coshell */ +#define CO_DEBUG 0x000002 /* library debug trace */ +#define CO_EXPORT 0x000004 /* export everything */ +#define CO_IGNORE 0x000008 /* ignore command errors */ +#define CO_LOCAL 0x000010 /* local affinity */ +#define CO_NONBLOCK 0x000020 /* don't block coexec if Q full */ +#define CO_SHELL 0x000040 /* shell using coshell! */ +#define CO_SILENT 0x000080 /* don't trace commands */ + +#define CO_KSH 0x000100 /* coshell is ksh (readonly) */ +#define CO_SERVER 0x000200 /* coshell is server (readonly) */ +#define CO_OSH 0x000400 /* coshell is OLD (readonly) */ + +#define CO_CROSS 0x000800 /* don't prepend local dirs */ +#define CO_DEVFD 0x001000 /* coshell handles /dev/fd/# */ + +#define CO_SERIALIZE 0x002000 /* serialize stdout and stderr */ +#define CO_SERVICE 0x004000 /* service callouts */ + +#define CO_APPEND 0x008000 /* append coexec() out/err */ +#define CO_SEPARATE 0x010000 /* 1 shell+wait per coexec() */ +#define CO_ORPHAN 0x020000 /* PROC_ORPHAN */ + +#define CO_USER 0x100000 /* first user flag */ + +struct Cojob_s /* coshell job info */ +{ + Coshell_t* coshell; /* running in this coshell */ + int id; /* job id */ + int status; /* exit status */ + int flags; /* CO_* flags */ + void* local; /* local info */ + unsigned long user; /* user time in 1/CO_QUANT secs */ + unsigned long sys; /* sys time in 1/CO_QUANT secs */ +#ifdef _CO_JOB_PRIVATE_ + _CO_JOB_PRIVATE_ /* library private additions */ +#endif +}; + +struct Coshell_s /* coshell connection info */ +{ + void* data; /* user data, initially 0 */ + int flags; /* flags */ + int outstanding; /* number of outstanding jobs */ + int running; /* number of running jobs */ + int total; /* number of coexec() jobs */ + unsigned long user; /* user time in 1/CO_QUANT secs */ + unsigned long sys; /* sys time in 1/CO_QUANT secs */ + Sfio_t* msgfp; /* message stream for sfpoll() */ +#ifdef _CO_SHELL_PRIVATE_ + _CO_SHELL_PRIVATE_ /* library private additions */ +#endif +}; + +extern int coclose(Coshell_t*); +extern Cojob_t* coexec(Coshell_t*, const char*, int, const char*, const char*, const char*); +extern char* coinit(int); +extern int coexport(Coshell_t*, const char*, const char*); +extern int cokill(Coshell_t*, Cojob_t*, int); +extern Coshell_t* coopen(const char*, int, const char*); +extern void coquote(Sfio_t*, const char*, int); +extern int cosync(Coshell_t*, const char*, int, int); +extern Cojob_t* cowait(Coshell_t*, Cojob_t*, int); + +extern int cojobs(Coshell_t*); +extern int copending(Coshell_t*); +extern int cozombie(Coshell_t*); + +extern int coattr(Coshell_t*, const char*); + +extern int coprocrun(const char*, char**, int); +extern int cosystem(const char*); + +#endif diff --git a/src/lib/libcoshell/costash.c b/src/lib/libcoshell/costash.c new file mode 100644 index 0000000..c378909 --- /dev/null +++ b/src/lib/libcoshell/costash.c @@ -0,0 +1,40 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + */ + +#include + +/* + * 0 terminate string stream, reset, and return value + */ + +char* +costash(Sfio_t* sp) +{ + char* s; + + if (!(s = sfstruse(sp))) + errormsg(state.lib, ERROR_LIBRARY|2, "out of space"); + return s; +} diff --git a/src/lib/libcoshell/cosync.c b/src/lib/libcoshell/cosync.c new file mode 100644 index 0000000..4439efa --- /dev/null +++ b/src/lib/libcoshell/cosync.c @@ -0,0 +1,127 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * sync all outstanding file operations for file opened on fd + * if file==0 then fd used + * if fd<0 then file used + * if mode<0 then fd not created + * + * NOTE: this is an unfortunate NFS workaround that should be done by fsync() + */ + +#include "colib.h" + +#include + +#include "FEATURE/nfsd" + +int +cosync(Coshell_t* co, const char* file, int fd, int mode) +{ +#if defined(_cmd_nfsd) + if (!co || (co->flags & CO_SERVER)) + { + char tmp[PATH_MAX]; + + if (file && *file) + { + register const char* s; + register char* t; + register char* b; + int td; + + /* + * writing to a dir apparently flushes the + * attribute cache for all entries in the dir + */ + + s = file; + b = t = tmp; + while (t < &tmp[sizeof(tmp) - 1]) + { + if (!(*t = *s++)) break; + if (*t++ == '/') b = t; + } + s = "..nfs..botch.."; + t = b; + while (t < &tmp[sizeof(tmp) - 1] && (*t++ = *s++)); + *t = 0; + if ((td = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0)) >= 0) close(td); + unlink(tmp); + if (fd >= 0 && mode >= 0) + { + if ((td = open(file, mode)) < 0) return(-1); + close(fd); + dup2(td, fd); + close(td); + } + } +#if defined(F_SETLK) + else + { + int clean = 0; + struct flock lock; + + if (fd < 0) + { + if (!file || mode < 0 || (fd = open(file, O_RDONLY)) < 0) return(-1); + clean = 1; + } + + /* + * this sets the VNOCACHE flag across NFS + */ + + lock.l_type = F_RDLCK; + lock.l_whence = 0; + lock.l_start = 0; + lock.l_len = 1; + if (!fcntl(fd, F_SETLK, &lock)) + { + lock.l_type = F_UNLCK; + fcntl(fd, F_SETLK, &lock); + } + if (clean) close(fd); + + /* + * 4.1 has a bug that lets VNOCACHE linger after unlock + * VNOCACHE inhibits mapping which kills exec + * the double rename flushes the incore vnode (and VNOCACHE) + * + * this kind of stuff doesn't happen with *real* file systems + */ + + if (file && *file) + { + strcpy(tmp, file); + fd = strlen(tmp) - 1; + tmp[fd] = (tmp[fd] == '*') ? '?' : '*'; + if (!rename(file, tmp)) rename(tmp, file); + } + } +#endif + } +#endif + return(0); +} diff --git a/src/lib/libcoshell/cowait.c b/src/lib/libcoshell/cowait.c new file mode 100644 index 0000000..ccc98d3 --- /dev/null +++ b/src/lib/libcoshell/cowait.c @@ -0,0 +1,411 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * wait for and return status of job or the next coshell job that completes + * job==co for non-blocking wait + */ + +#include "colib.h" + +#include + +/* + * cat and remove fd {1,2} serialized output + */ + +static void +cat(Cojob_t* job, char** path, Sfio_t* op) +{ + Sfio_t* sp; + + if (sp = sfopen(NiL, *path, "r")) + { + sfmove(sp, op, SF_UNBOUND, -1); + sfclose(sp); + } + else + errormsg(state.lib, ERROR_LIBRARY|2, "%s: cannot open job %d serialized output", *path, job->id); + remove(*path); + free(*path); + *path = 0; +} + +/* + * the number of running+zombie jobs + * these would count against --jobs or NPROC + */ + +int +cojobs(Coshell_t* co) +{ + int any; + int n; + + if (co) + any = 0; + else if (!(co = state.coshells)) + return -1; + else + any = 1; + n = 0; + do + { + n += co->outstanding; + } while (any && (co = co->next)); + return n; +} + +/* + * the number of pending cowait()'s + */ + +int +copending(Coshell_t* co) +{ + int any; + int n; + + if (co) + any = 0; + else if (!(co = state.coshells)) + return -1; + else + any = 1; + n = 0; + do + { + n += co->outstanding + co->svc_outstanding; + } while (any && (co = co->next)); + return n; +} + +/* + * the number of completed jobs not cowait()'d for + * cowait() always reaps the zombies first + */ + +int +cozombie(Coshell_t* co) +{ + int any; + int n; + + if (co) + any = 0; + else if (!(co = state.coshells)) + return -1; + else + any = 1; + n = 0; + do + { + n += (co->outstanding + co->svc_outstanding) - (co->running + co->svc_running); + } while (any && (co = co->next)); + return n; +} + +Cojob_t* +cowait(register Coshell_t* co, Cojob_t* job, int timeout) +{ + register char* s; + register Cojob_t* cj; + register Coservice_t* cs; + register ssize_t n; + char* b; + char* e; + unsigned long user; + unsigned long sys; + int active; + int any; + int id; + int loop; + int to; + int type; + char buf[128]; + + static unsigned long serial = 0; + + serial++; + if (co || job && (co = job->coshell)) + any = 0; + else if (!(co = state.coshells)) + goto echild; + else + any = 1; + + /* + * first drain the zombies + */ + + active = 0; + to = timeout >= 0 ? timeout : 60 * 1000; + zombies: + do + { +#if 0 + errormsg(state.lib, 2, "coshell %d zombie wait %lu timeout=%d outstanding=<%d,%d> running=<%d,%d>", co->index, serial, timeout, co->outstanding, co->svc_outstanding, co->running, co->svc_running); +#endif + if ((co->outstanding + co->svc_outstanding) > (co->running + co->svc_running)) + for (cj = co->jobs; cj; cj = cj->next) + if (cj->pid == CO_PID_ZOMBIE && (!job || cj == job)) + { + cj->pid = CO_PID_FREE; + if (cj->service) + co->svc_outstanding--; + else + co->outstanding--; +#if 0 + errormsg(state.lib, 2, "coshell %d zombie wait %lu timeout=%d outstanding=<%d,%d> running=<%d,%d> reap job %d", co->index, serial, timeout, co->outstanding, co->svc_outstanding, co->running, co->svc_running, cj->id); +#endif + return cj; + } + else if (cj->service && !cj->service->pid) + { + cj->pid = CO_PID_ZOMBIE; + cj->status = 2; + cj->service = 0; + co->svc_running--; + } + if (co->running > 0) + active = 1; + else if (co->svc_running > 0) + { + n = 0; + for (cs = co->service; cs; cs = cs->next) + if (cs->pid && kill(cs->pid, 0)) + { + cs->pid = 0; + close(cs->fd); + cs->fd = -1; + n = 1; + } + if (n) + goto zombies; + active = 1; + } + } while (any && (co = co->next)); + + /* + * reap the active jobs + */ + + if (!active) + goto echild; + if (any) + co = state.coshells; + do + { + loop = 0; + for (;;) + { + if (co->flags & CO_DEBUG) + { + loop++; + errormsg(state.lib, 2, "coshell %d wait %lu.%d timeout=%d outstanding=<%d,%d> running=<%d,%d>", co->index, serial, loop, timeout, co->outstanding, co->svc_outstanding, co->running, co->svc_running); + for (cj = co->jobs; cj; cj = cj->next) + if (cj->pid != CO_PID_FREE) + errormsg(state.lib, 2, "\tjob %d pid=%d status=%d", cj->id, cj->pid, cj->status); + } + if (co->running <= 0) + break; + while ((n = sfpoll(&co->msgfp, 1, to)) < 1) + { + if (n < 0) + { + if (errno == EINTR) + return 0; + break; + } + if (timeout >= 0) + break; + + /* + * check for a killed job with no status + */ + + for (cj = co->jobs; cj; cj = cj->next) + if (cj->pid > 0) + { + n = sfsprintf(buf, sizeof(buf), "kill -0 %d 2>/dev/null || echo k %d `wait %d 2>/dev/null; echo $?` >&$%s\n", cj->pid, cj->id, cj->pid, CO_ENV_MSGFD); + write(co->cmdfd, buf, n); + break; + } + } + + /* + * get one coshell message + */ + + if (!(s = b = sfgetr(co->msgfp, '\n', 1))) + break; +#if 0 + errormsg(state.lib, 2, "coshell %d active wait %lu timeout=%d outstanding=<%d,%d> running=<%d,%d>", co->index, serial, timeout, co->outstanding, co->svc_outstanding, co->running, co->svc_running); +#endif + + /* + * read and parse a coshell message packet of the form + * + * + * %c %d %s %c + */ + + while (isspace(*s)) + s++; + if (!(type = *s) || type != 'a' && type != 'j' && type != 'k' && type != 'x') + goto invalid; + while (*++s && !isspace(*s)); + id = strtol(s, &e, 10); + if (*e && !isspace(*e)) + goto invalid; + for (s = e; isspace(*s); s++); + + /* + * locate id in the job list + */ + + for (cj = co->jobs; cj; cj = cj->next) + if (id == cj->id) + break; + if ((co->flags | (cj ? cj->flags : 0)) & CO_DEBUG) + errormsg(state.lib, 2, "coshell %d message \"%c %d %s\"", co->index, type, id, s); + if (!cj) + { + if (type == 'k') + continue; + errormsg(state.lib, 2, "coshell %d job id %d not found [%s]", co->index, id, b); + errno = ESRCH; + return 0; + } + + /* + * now interpret the message + */ + + switch (type) + { + + case 'a': + /* + * coexec() ack + */ + + if (cj == job) + return cj; + break; + + case 'j': + /* + * is the job pid + */ + + n = cj->pid; + cj->pid = strtol(s, NiL, 10); + if (n == CO_PID_WARPED) + goto nuke; + break; + + case 'k': + /* + * is a synthesized killed status + */ + + if (cj->pid < 0) + continue; + /*FALLTHROUGH*/ + + case 'x': + /* + * is the job exit code and user,sys times + */ + + cj->status = strtol(s, &e, 10); + user = sys = 0; + for (;;) + { + if (e <= s) + break; + for (s = e; isalpha(*s) || isspace(*s); s++); + user += strelapsed(s, &e, CO_QUANT); + if (e <= s) + break; + for (s = e; isalpha(*s) || isspace(*s); s++); + sys += strelapsed(s, &e, CO_QUANT); + } + cj->user += user; + cj->sys += sys; + co->user += user; + co->sys += sys; + if (cj->out) + cat(cj, &cj->out, sfstdout); + if (cj->err) + cat(cj, &cj->err, sfstderr); + if (cj->pid > 0 || cj->service || (co->flags & (CO_INIT|CO_SERVER))) + { + nuke: + if (cj->pid > 0 && type != 'k') + { + /* + * nuke the zombies + */ + + n = sfsprintf(buf, sizeof(buf), "wait %d\n", cj->pid); + write(co->cmdfd, buf, n); + } + if (cj->service) + co->svc_running--; + else + co->running--; + if (!job || cj == job) + { + cj->pid = CO_PID_FREE; + if (cj->service) + co->svc_outstanding--; + else + co->outstanding--; +#if 0 + errormsg(state.lib, 2, "coshell %d active wait %lu timeout=%d outstanding=<%d,%d> running=<%d,%d> reap job %d", co->index, serial, timeout, co->outstanding, co->svc_outstanding, co->running, co->svc_running, cj->id); +#endif + return cj; + } + cj->pid = CO_PID_ZOMBIE; + } + else + cj->pid = CO_PID_WARPED; + break; + + } + } + } while (any && (co = co->next)); + return 0; + echild: +#if 0 + errormsg(state.lib, 2, "coshell wait ECHILD"); +#endif + errno = ECHILD; + return 0; + invalid: + errormsg(state.lib, 2, "coshell %d invalid message \"%-.*s>>>%s<<<\"", co->index, s - b, b, s); + errno = EINVAL; + return 0; +} diff --git a/src/lib/libcoshell/ignore.sh b/src/lib/libcoshell/ignore.sh new file mode 100644 index 0000000..f70002e --- /dev/null +++ b/src/lib/libcoshell/ignore.sh @@ -0,0 +1,45 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1990-2011 AT&T Intellectual Property # +# and is licensed under the # +# Eclipse Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.eclipse.org/org/documents/epl-v10.html # +# (with md5 checksum b35adb5213ca9657e911e9befb180842) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# Glenn Fowler # +# # +######################################################################## +: +# +# Glenn Fowler +# AT&T Bell Laboratories +# +# Bourne coshell support +# +# @(#)ignore (AT&T Bell Laboratories) 08/11/92 +# +while : +do case $# in + 0) exit 0 ;; + esac + case $1 in + *=*) case $RANDOM in + $RANDOM)`echo $1 | sed "s/\\([^=]*\\)=\\(.*\\)/eval \\1='\\2'; export \\1/"` ;; + *) export "$1" ;; + esac + shift + ;; + *) break + ;; + esac +done +"$@" +exit 0 diff --git a/src/lib/libcoshell/procrun.c b/src/lib/libcoshell/procrun.c new file mode 100644 index 0000000..cdd5357 --- /dev/null +++ b/src/lib/libcoshell/procrun.c @@ -0,0 +1,56 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * coshell procrun(3) + */ + +#include "colib.h" + +#include + +int +coprocrun(const char* path, char** argv, int flags) +{ + register char* s; + register char** a; + register Sfio_t* tmp; + int n; + + if (!(a = argv)) + return procclose(procopen(path, a, NiL, NiL, PROC_FOREGROUND|PROC_GID|PROC_UID|flags)); + if (!(tmp = sfstropen())) + return -1; + sfputr(tmp, path ? path : "sh", -1); + while (s = *++a) + { + sfputr(tmp, " '", -1); + coquote(tmp, s, 0); + sfputc(tmp, '\''); + } + if (!(s = costash(tmp))) + return -1; + n = cosystem(s); + sfstrclose(tmp); + return n; +} diff --git a/src/lib/libcoshell/silent.sh b/src/lib/libcoshell/silent.sh new file mode 100644 index 0000000..1f3f4b7 --- /dev/null +++ b/src/lib/libcoshell/silent.sh @@ -0,0 +1,44 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1990-2011 AT&T Intellectual Property # +# and is licensed under the # +# Eclipse Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.eclipse.org/org/documents/epl-v10.html # +# (with md5 checksum b35adb5213ca9657e911e9befb180842) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# Glenn Fowler # +# # +######################################################################## +: +# +# Glenn Fowler +# AT&T Bell Laboratories +# +# Bourne coshell support +# +# @(#)silent (AT&T Bell Laboratories) 08/11/92 +# +while : +do case $# in + 0) exit 0 ;; + esac + case $1 in + *=*) case $RANDOM in + $RANDOM)`echo $1 | sed "s/\\([^=]*\\)=\\(.*\\)/eval \\1='\\2'; export \\1/"` ;; + *) export "$1" ;; + esac + shift + ;; + *) break + ;; + esac +done +"$@" diff --git a/src/lib/libcoshell/system.c b/src/lib/libcoshell/system.c new file mode 100644 index 0000000..9179ef8 --- /dev/null +++ b/src/lib/libcoshell/system.c @@ -0,0 +1,57 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * coshell system(3) + */ + +#include "colib.h" + +int +cosystem(const char* cmd) +{ + Coshell_t* co; + Cojob_t* cj; + int status; + + if (!cmd) + return !eaccess(pathshell(), X_OK); + if (!(co = coopen(NiL, CO_ANY, NiL))) + return -1; + if (cj = coexec(co, cmd, CO_SILENT, NiL, NiL, NiL)) + cj = cowait(co, cj, -1); + if (!cj) + return -1; + + /* + * synthesize wait() status from shell status + * lack of synthesis is the standard's proprietary sellout + */ + + status = cj->status; + if (EXITED_TERM(status)) + status &= ((1<<(EXIT_BITS-1))-1); + else + status = (status & ((1<