COSHELL(3)

๐Ÿ“ฆ version 93vu, in opensuse-leap 16.0

โœจ coshell(3) โ€” shell coprocess support โœจ

okay bestie, so this is coshell(3) from the Library Functions Manual, and it's literally the whole "run a shell as a coprocess" situation. Sit down, grab a lil drink ๐Ÿ“, we're doing this.


๐ŸŒธ NAME ๐ŸŒธ

coshell โ€” shell coprocess support ๐Ÿ’…


๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

#include <coshell.h>
-lcoshell -last

link it up with -lcoshell -last, no cap.


๐Ÿ’– DESCRIPTION ๐Ÿ’–

The coshell routines are the whole support system for treating a shell as a coprocess. And like, this coprocess can be either ksh(1) or sh(1) running on the local host, or it can be coshell(1) with access to shells on hosts all over your local network. it's giving networking era ๐ŸŒˆ

The coshell inherits the environment of the calling process. And signals sent to the calling process are passed along to the coshell, too โ€” they stay in sync, periodt.

Also bestie, you can have more than one coshell open in the current process. and here's the slay part: if the Coshell_t* argument to cowait(), cokill(), copending(), cozombie(), or coclose() is 0, then the call gets applied to all open coshells. all of them. every single one ๐Ÿ‘‘


๐Ÿ‘‘ the opening move: coopen ๐Ÿ‘‘

Coshell_t* coopen(const char* shell, int flags, const char* attributes)

Returns a pointer to a new coshell. NULL comes back on error ๐Ÿ˜ญ.

If shell is NULL then the coshell executable is figured out by doing the usual path search, in order, on the value of the environment variable COSHELL and the commands ksh and sh. iykyk.

flags is the inclusive-or of these icons:

  • CO_ANY โ€” return a pointer to a previously opened coshell if possible; otherwise open a brand new coshell. reusing is giving sustainable queen โ™ป๏ธ
  • CO_DEBUG โ€” turn on library debug tracing.
  • CO_IGNORE โ€” ignore any command errors. by default, any command error that isn't tested by a conditional causes the job to terminate. so this is the "no thoughts head empty" flag ๐Ÿ’€
  • CO_LOCAL โ€” commands are to be executed on the local host only. stay home bestie ๐Ÿ 
  • CO_SHELL โ€” the caller is sh(1): internal file descriptors get moved to 10 or above, and SIGSTOP and SIGCONT handlers are not installed.
  • CO_SILENT โ€” don't trace commands. by default commands are traced using the shell -x option. (so this is the "stop narrating my life" flag ๐Ÿ™ˆ)
  • CO_NONBLOCK โ€” normally coexec() blocks when the job queue is full and waits until a job completes. CO_NONBLOCK makes coexec() return NULL when the job queue is full instead of waiting. no queuing, we die like icons ๐Ÿ’…

attributes is a string that gets interpreted by the coshell. If attributes is NULL then the value of the environment variable COATTRIBUTES is used if it's defined. ksh and sh ignore this string entirely (rude but ok).

The return value points to a structure with these readonly elements:

  • int flags โ€” the default flags.
  • int outstanding โ€” the number of jobs that have not been waited for.
  • int running โ€” the number of jobs still running.
  • int total โ€” the total number of jobs sent to the coshell.
  • unsigned long user โ€” the total user time of all completed jobs, in 1/CO_QUANT second increments.
  • unsigned long sys โ€” the total system time of all completed jobs, in 1/CO_QUANT second increments.

๐ŸŽ€ coclose โ€” the goodbye ๐ŸŽ€

int coclose(Coshell_t* sh)

Closes an open coshell pointed to by sh, and the coshell exit status is returned. it's giving closure, literally ๐Ÿซถ


๐Ÿ”ฅ coexec โ€” send it ๐Ÿ”ฅ

Cojob_t* coexec(Coshell_t* sh, const char* cmd, int flags, const char* out, const char* err, const char* att)

Sends the shell command line cmd to the open coshell pointed to by sh for execution. go little command go ๐Ÿ

  • flags are the same as in the coopen() call, and are used to augment the default settings from coopen().
  • out is the standard output file name and defaults to stdout if NULL.
  • err is the standard error file name and defaults to stderr if NULL.
  • att, if non-NULL, contains job attributes that are appended to the attributes from coopen() before being sent to the coshell.

The return value points to a structure with these elements:

  • int id โ€” a number that uniquely identifies the job within the coshell.
  • int status โ€” the job exit status, valid only for job pointers returned by cowait().
  • int flags โ€” the flags enabled for this job.
  • void* local โ€” a user reserved pointer, initially set to NULL on return from coexec(). This is the only job field that may be modified by the user. The user defined value is preserved until after the cowait() call that returns the job pointer. it's your little pocket, protect it ๐ŸŽ’
  • unsigned long user โ€” the user time of this job in 1/CO_QUANT second increments, valid only for job pointers returned by cowait().
  • unsigned long sys โ€” the system time of this job in 1/CO_QUANT second increments, valid only for job pointers returned by cowait().

โญ cowait โ€” the waiting game โญ

Cojob_t* cowait(Coshell_t* sh, Cojob_t* job, int timeout)

Returns the job pointer in the coshell pointed to by sh for the job pointed to by job. If job is NULL then a pointer to any completed job is returned. cowait() blocks until the specified job(s) complete โ€” so yes, it will just stand there. patiently. like a bestie holding your bag ๐Ÿ‘œ

NULL is returned on error, or if all jobs have completed. and errno gets set to:

  • EINVAL for coshell communication errors
  • ECHILD if job is NULL and there are no children
  • ESRCH if job is not NULL and it's not an active job

cozombie(sh) is the number of jobs that may be waited for without blocking. The return value status, user and sys job fields are set to their final values. The return value is valid until the next coexec(), cowait() or coclose() call โ€” so don't go hoarding it, it expires ๐Ÿ˜ญ

timeout is the maximum time in milliseconds that wait will block. If the wait times out then 0 is returned. A negative timeout waits until a job completes or a signal is received. negative timeout is the "I have all the time in the world" era ๐Ÿฆ‹


๐Ÿ’œ the little counters ๐Ÿ’œ

  • 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, ok? don't @ me)
  • int copending(Coshell_t* sh) โ€” returns the number of pending jobs; this is the number of cowait() calls required to reap all running jobs. math queen ๐Ÿงฎ
  • int cozombie(Coshell_t* sh) โ€” returns the number of jobs that have completed but have not been cowait()'d for. the undead jobs ๐ŸงŸ

๐Ÿงš cokill โ€” sending the vibes (and signals) ๐Ÿงš

int cokill(Coshell_t* sh, Cojob_t* job, int sig)

The signal sig is sent to the job pointed to by job running in the coshell pointed to by sh.

  • If job is NULL then the signal is sent to all jobs in the coshell.
  • If both sh and job are NULL then the signal is sent to all jobs in all coshells. yes, all of them. chaos mode activated ๐Ÿ’€

-1 is returned on error, 0 otherwise.


๐ŸŒˆ cosync โ€” the NFS workaround era ๐ŸŒˆ

int cosync(Coshell_t* sh, const char* path, int fd, int mode)

Sync all outstanding file operations for either the file path or the file descriptor fd, after its shell action has completed in sh.

  • If path is NULL then fd is used.
  • If fd<0 and mode>=0 then path is opened using mode.

This is an unfortunate workaround for NFS and remote coshells, and it's a no-op for all real file systems. It should be called after cowait() to ensure that all file system cache info has been flushed. But heads up bestie: sync(2) or fsync(2) must still be called to schedule file data to be written to disk. so it's not a substitute, it's a supplement ๐Ÿ’…


๐Ÿ“ coinit โ€” get the init commands ๐Ÿ“

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 coinit(), e.g. current working directory or umask.

If sh is local (a child of the calling process) coinit() may return the empty string; if 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. so like, if you're here, you're built different ๐Ÿซก


๐Ÿ’… coquote โ€” the quoting queen ๐Ÿ’…

void coquote(Sfio_T* sp, const char* string, int type)

Applies shell single quoting to string and copies the result into the sfio stream sp.

If type!=0 then any occurence of /hosttype/ is translated to /$HOSTTYPE/, where hosttype is the current value of the HOSTTYPE environment variable. (yes, "occurence" โ€” we're keeping it exactly as written, don't come for me ๐Ÿ™ˆ)

This routine is used by remote coshell implementations and is not normally called from user code. same energy as above. iconic but off-limits ๐Ÿ’œ


๐ŸŽ€ CAVEATS ๐ŸŽ€

cosync() is a hack workaround, but we do have to work in the real world. ๐Ÿ˜ญ reality is unfortunately real.
A bug in bsh(1) and 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. so if you see a 0 where you expected pain, that's the bug, not you ๐Ÿ’€
trap 0 is reserved by coexec() at the outermost scope. To use trap 0 use (...) to force a subshell. don't fight it, just wrap it in parens bestie ๐ŸŽ

๐ŸŒธ SEE ALSO ๐ŸŒธ

coshell(1), ksh(1), nmake(1), sh(1), cs(3), libast(3)

read them, love them, link them ๐Ÿ’–


okay that's the whole page, you ate that. this was coshell(3) out of the Library Functions Manual โ€” go forth and coprocess, bestie. mwah ๐Ÿ’‹โœจ๐ŸŽ€

๐Ÿ“ฆ where this page comes from