Bourne | Ash |  #!  | find | ARG_MAX | Shells | portability | permissions | UUOC | ancient | - | ../Various | HOME
"$@" | echo/printf | set -e | test | tty defs | tty chars | $() vs ) | IFS | using siginfo | nanosleep | line charset | locale


Back to the main page.

I am glad that the following is available in the TUHS Research Unix archive.
The source code was released by Alcatel-Lucent with a Statement Regarding Research Unix Editions 8, 9, and 10 about non-commercial usage.

Here are extracts from 8th edition /usr/sys/h/user.h and /usr/src/sys/sys1.c with some arbitrary emphasis in bold face.


user.h:

#define SHSIZE  32

struct  user
{
[...]
        union {
           struct {                     /* header of executable file */
                int     Ux_mag;         /* magic number */
                unsigned Ux_tsize;      /* text size */
                unsigned Ux_dsize;      /* data size */
                unsigned Ux_bsize;      /* bss size */
                unsigned Ux_ssize;      /* symbol table size */
                unsigned Ux_entloc;     /* entry location */
                unsigned Ux_unused;
                unsigned Ux_relflg;
           } Ux_A;
           char ux_shell[SHSIZE];       /* #! and name of interpreter */
        } u_exdata;
[...]
};


sys1.c


/*
 * exec system call, with and without environments.
 */
struct execa {
	char	*fname;
	char	**argp;
	char	**envp;
};

exec()
{
	((struct execa *)u.u_ap)->envp = NULL;
	exece();
}

struct shdata {
	struct direct	sd_save;
	int		sd_gid;
	int		sd_uid;
	int		sd_indir;
	char		sd_flag[SHSIZE];
};

struct swargs {
	char		*sw_cp;
	int		sw_argc;
	int		sw_envc;
	int		sw_chars;
	int		sw_bn;
	struct buf	*sw_bp;
};

[...]

exece()
{
	register int		i;
	register int		ap;
	register int		bsize;
	register char		*cp;
	register char		*ucp;
	register struct execa	*uap;
	struct buf		*bp;
	struct inode		*ip;
	struct shdata		sh;
	struct swargs		sw;
	extern struct inode	*gethead();

	sh.sd_uid = u.u_uid;
	sh.sd_gid = u.u_gid;
	sh.sd_indir = 0;
	sh.sd_flag[0] = '\0';

	if ((ip = gethead(&sh)) == NULL)
		return;

	[...]
}

/*
 *	Get a shell after #! magic number.
 */
struct inode	*
getshell(ip, sp)
struct inode	*ip;
struct shdata	*sp;
{
	register char		*cp;
	extern struct inode	*gethead();

	if(u.u_exdata.ux_shell[0] != '#' || u.u_exdata.ux_shell[1] != '!' || sp->sd_indir)
		goto error;
	cp = &u.u_exdata.ux_shell[2];
	for (;;) {
		if (cp == &u.u_exdata.ux_shell[SHSIZE])
			goto error;
		if (*cp == '\n') {
			*cp = '\0';
			break;
		}
		if (*cp == '\t')
			*cp = ' ';
		cp++;
	}
	for (cp = &u.u_exdata.ux_shell[2]; *cp == ' '; cp++);
	if (*cp == '\0')
		goto error;
	u.u_dirp = cp;
	while (*cp != '\0') {
		if (*cp == ' ') {
			*cp++ = '\0';
			while (*cp == ' ')
				cp++;
			/*
			 *	Shell argument (one only).
			 */
			if (*cp != '\0') {
				register int	i;

				i = 0;
				do
					sp->sd_flag[i++] = *cp++;
				while (*cp != ' ' && *cp != '\0');
				sp->sd_flag[i] = '\0';
			}
			break;
		}
		else
			cp++;
	}
	iput(ip);
	sp->sd_save = u.u_dent;
	sp->sd_indir = 1;
	return gethead(sp);

error:
	iput(ip);
	u.u_error = ENOEXEC;
	return NULL;
}

/*
 *	Get the header of an executable and do all the right tests.
 */
struct inode	*
gethead(sp)
struct shdata	*sp;
{
	register struct inode	*ip;

	if ((ip = namei(sp->sd_indir ? schar : uchar, 0, 1)) == NULL)
		return NULL;

	/*
	 *	Setuid and setgid denied for network root.
	 */
	if ((ip->i_mode & ISUID) != 0 && ip->i_uid != -1)
		sp->sd_uid = ip->i_uid;

	if ((ip->i_mode & ISGID) != 0 && ip->i_gid != -1)
		sp->sd_gid = ip->i_gid;

	/*
	 *	Check permission.  May not trace something we can't read.
	 */
	if(access(ip, IEXEC) || (PTRACED(u.u_procp) && access(ip, IREAD)))
		goto out;

	/*
	 *	Must be a regular file and must really be executable.
	 */
	if ((ip->i_mode & IFMT) != IFREG ||
	    (ip->i_mode & (IEXEC | (IEXEC >> 3) | (IEXEC >> 6))) == 0) {
		u.u_error = EACCES;
		goto out;
	}

	/*
	 * ux_mag = 407/410/413
	 *	407 is plain executable
	 *	410 is RO text
	 *	413 is demand paged RO text
	 *
	 * Also an ASCII line beginning with #! is
	 * the file name of a ``shell'' and arguments may be prepended
	 * to the argument list if given here.
	 *
	 * Shell names are limited in length.
	 *
	 * Only one argument may be passed to the shell from the ASCII line.
	 */
	u.u_base = (caddr_t)&u.u_exdata;
	u.u_count = sizeof(u.u_exdata);
	u.u_offset = 0;
	u.u_segflg = 1;
	readi(ip);
	u.u_segflg = 0;

	if (u.u_error)
		goto out;

	if (u.u_count > sizeof (u.u_exdata) - sizeof (u.u_exdata.Ux_A))
		ip = getshell(ip, sp);
	else {
		switch (u.u_exdata.ux_mag) {
		case 0407:
			u.u_exdata.ux_dsize += u.u_exdata.ux_tsize;
			u.u_exdata.ux_tsize = 0;
			break;

		case 0410:
		case 0413:
			if (u.u_exdata.ux_tsize == 0)
				u.u_error = ENOEXEC;
			break;

		default:
			ip = getshell(ip, sp);
		}
	}

out:
	if (u.u_error && ip != NULL) {
		iput(ip);
		ip = NULL;
	}

	return ip;
}

[...]