[net.lang.c] setenv from c

rlj@ncsu.UUCP (Rick Johnson) (09/19/85)

Is it possible to set a csh environment variable within a C program? I know
about getenv(), but have failed at attempts to set an environment variable
from a C program. Basically, what I want to do is "source" a new environment
variable for the current csh process. Any comments or suggestions would be
appreciated. Thanks.


-- 
 
Rick Johnson   (decvax!mcnc!ncsu!rlj)

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/22/85)

> Is it possible to set a csh environment variable within a C program? I know
> about getenv(), but have failed at attempts to set an environment variable
> from a C program. Basically, what I want to do is "source" a new environment
> variable for the current csh process. Any comments or suggestions would be
> appreciated. Thanks.

There seems to be some confusion here.  A program invoked from the
shell is a child process of the shell, not "the current process".
A child process cannot affect the environment of its parent, so the
answer is "No".

The environment is not a csh-specific concept; see ENVIRON(5 or 4).

tmb@talcott.UUCP (Thomas M. Breuel) (09/22/85)

In article <2936@ncsu.UUCP>, rlj@ncsu.UUCP (Rick Johnson) writes:
> Is it possible to set a csh environment variable within a C program? I know
> about getenv(), but have failed at attempts to set an environment variable
> from a C program. Basically, what I want to do is "source" a new environment
> variable for the current csh process. Any comments or suggestions would be
> appreciated. Thanks.

[I have heard this question before several times, and think that
a posting is therefore warranted. It belongs in net.unix.]

[see also execve(2) and environ(7)]

The environment strings reside in a process' address space,
which makes them inaccessible to any other process. It is therefore
not possible for one process to change the environment variables of
another process except at the time of creation of an immediate child,
when the environment is passed as the third argument to the execve
system call (other varieties of the exec system call (implemented
as library functions) pass the environment data along automatically).

						Thomas.

myke@gitpyr.UUCP (Myke Reynolds) (09/23/85)

Well, the following program will pass a parent's environment variables to
a child. It nukes the first env var and replaces it with a new one.
What you want to do is change the internal variables of a parent process.
It will only do that if it wants to let you, there is no inherent way of
doing it. Note that setenv and set are shell commands, not executable
files..

---------------------------------------------------------------------------------
char	*newvar = "newvar = (here I am !)";

main(argc, argv, envp)
char	**argv, **envp;
{
	envp[0] = newvar;

	execle("/bin/csh", "csh", 0, envp);
}
-- 
Myke Reynolds
Office of Telecommunications and Networking
Georgia Insitute of Technology, Atlanta Georgia, 30332
...!{akgua,allegra,amd,hplabs,ihnp4,seismo,ut-ngp}!gatech!gitpyr!myke


"Too bad all the people that know how to run this country are busy cutting
hair and driving taxi cabs."
		-George Burns

mouse@mcgill-vision.UUCP (der Mouse) (09/23/85)

> Is it possible to set a csh environment variable within a C program? I know
> about getenv(), but have failed at attempts to set an environment variable
> from a C program. Basically, what I want to do is "source" a new environment
> variable for the current csh process. Any comments or suggestions would be
> appreciated. Thanks.

     In general, this  is possible theoretically (most UNIX systems) but
impossible in practice.  Now before you get disheartened, note that with
a little cooperation  from the shell this is quite feasible.   There are
at least two ways:

(1) If the  program  doesn't want to  print anything, have it just print
the  command  the shell should execute and then  use  backquotes  in the
shell  to catch this.  Then  you can "eval" it (you did say  csh, didn't
you...yes).

(2) If the program wants to  do printing, have  it write a file on  /tmp
somewhere  (eg, use  getppid() to  find the parent shell's  PID and  use
that) that the shell can find (in  the  example, /tmp/$$ or some  such).
Then  the  "command"  users  see is  actually an  alias which  runs  the
program, sources the temp file, and removes it.  Something like that.

     If you aren't interested in hairy reasons or explanations, you  can
stop reading now.

     The reason for this is  that  there is  no way provided  for  the C
program to  do anything  to its  parent  process, such as  modifying the
environment stored  there.   The reason it's possible  theoretically  is
that (theory  only,  note) the C  program can  go  poking around  in the
kernel tables (via /dev/kmem or equivalent) and find out where the shell
process lives  (in memory  or  on  swap)  and  modify it  directly  (via
/dev/mem or /dev/drum or equivalent).  This requires more privilege than
user programs usually have in the first place and it would be difficult,
if not impossible,  to avoid timing  problems  (it's on swap,  ok, clock
ticks and it's in memory, but we still think it's on swap).

     I've  tried to redirect followups  to net.unix (hope it works, this
is the first time I've tried to do that).
-- 
					der Mouse

{ihnp4,decvax,akgua,etc}!utcsri!mcgill-vision!mouse
philabs!micomvax!musocs!mcgill-vision!mouse

Hacker: One responsible for destroying /
Wizard: One responsible for recovering it afterward

mash@mips.UUCP (John Mashey) (09/23/85)

Rick Johnson (decvax!mcnc!ncsu!rlj) writes:
> Is it possible to set a csh environment variable within a C program? I know
> about getenv(), but have failed at attempts to set an environment variable
> from a C program. Basically, what I want to do is "source" a new environment
> variable for the current csh process. Any comments or suggestions would be
> appreciated. Thanks.

I assume that you're not interested in just modifying the current environment,
[which is fairly straightforward, and has been posted somewhere in last
6 months], but in getting changes back to the parent shell [csh or sh].

You can't modify the parent shell's environment directly, but only by
returning a character string to be "eval"ed.  Suppose C pgm "glurp" ends with:
	printf("setenv a b\n"); 
Invoke it with: 	eval `glurp`
to set a = b.  More complex effects can be gotten by changing the printf string.

A process cannot modify its parent's environment, on purpose.  At one point,
such a feature was considered, and rejected, because it had no simple,
understandable semantics.  For example, many otherwise deterministic
computations turn nondeterministic, depending on process scheduling.
Consider a pipeline like a | b | c, where all of a, b, c could modify
their parent's environment, or worse, nohup a& (and logoff).  [Exactly
what is a modifying?]
-- 
-john mashey
UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!mash
DDD:  	415-960-1200
USPS: 	MIPS Computer Systems, 1330 Charleston Rd, Mtn View, CA 94043

cottrell@nbs-vms.ARPA (COTTRELL, JAMES) (09/24/85)

/*
> Is it possible to set a csh environment variable within a C program? I know
> about getenv(), but have failed at attempts to set an environment variable
> from a C program. Basically, what I want to do is "source" a new environment
> variable for the current csh process. Any comments or suggestions would be
> appreciated. Thanks.

In a word NO! There is no way to affect your parent's environment unless
they made prior arrangements (like before you were born) to do so. 
Sounds kinda like Life, doesn't it?

A way to do this is to set up an alias which runs your program,
then sources a file which the program builds. Try the following:

	alias	z	'echo setenv QAZ WSX > EDC ; source EDC'

Now type `printenv;z;printenv'. Note the change!
Another method might be:

	setenv	NAME	`prog`

if you only want you change ONE variable & you know it's name. If you
want to change more, you can always do:

	set x = `prog` ; $x

I don't know why `prog` by itself on a line doesn't work. Probably
because `...` only returns one word. Oh well...

	jim		cottrell@nbs
*/
------

david@ukma.UUCP (David Herron, NPR Lover) (09/25/85)

> > Is it possible to set a csh environment variable within a C program? I know
> > about getenv(), but have failed at attempts to set an environment variable
> > from a C program. Basically, what I want to do is "source" a new environment
> > variable for the current csh process. Any comments or suggestions would be
> > appreciated. Thanks.

As many people have said, it ain't possible to set your parents' environment
(that is, short of such self-abuse as poking around in /dev/mem).

However, it *is* possible to do it for your children.  See my posting
in net.sources for more information.
-- 
--- David Herron
--- ARPA-> ukma!david@ANL-MCS.ARPA
--- UUCP-> {ucbvax,unmvax,boulder,oddjob}!anlams!ukma!david
---        {ihnp4,decvax,ucbvax}!cbosgd!ukma!david

Hackin's in me blood.  My mother was known as Miss Hacker before she married!

pdg@ihdev.UUCP (P. D. Guthrie) (09/25/85)

>Rick Johnson (decvax!mcnc!ncsu!rlj) writes:
>> Is it possible to set a csh environment variable within a C program? I know
>> about getenv(), but have failed at attempts to set an environment variable
>> from a C program. Basically, what I want to do is "source" a new environment
>> variable for the current csh process. Any comments or suggestions would be
>> appreciated. Thanks.
>
>A process cannot modify its parent's environment, on purpose.  At one point,
>such a feature was considered, and rejected, because it had no simple,
>understandable semantics.  For example, many otherwise deterministic
>computations turn nondeterministic, depending on process scheduling.
>Consider a pipeline like a | b | c, where all of a, b, c could modify
>their parent's environment, or worse, nohup a& (and logoff).  [Exactly
>what is a modifying?]
>-- 
>-john mashey
>UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!mash
>DDD:  	415-960-1200
>USPS: 	MIPS Computer Systems, 1330 Charleston Rd, Mtn View, CA 94043

What about implementing some type of library function and corresponding
shell commands that would load and save an "environment", including
perhaps aliases, sort of like Carnegie-Mellon's PCL-Exec for Tops-20.
This would enable someone to modify the environment from a program and
then have the shell load it in, so file locking could be used to guard
against multiple modifications. I guess this capability crudely exists 
with the source command, but I was thinking of something at a lower level
with less command interpretation. I really liked PCL-Exec, and something
with that power would be great on Unix. Perhaps a signal could be used to
notify a process that it has a new environment?

Paul Guthrie.

rs@mirror.UUCP (09/25/85)

> Is it possible to set a csh environment variable within a C program?

Presumably you want to do something like the BSD "tset" program, which
SEEMS LIKE it adds environment variables to its parent's process.

The only way to do this is to have your program write out a command
line that can be "sourced" in.  For example, this program:

	main()
	{
	    int p;

	    if (p = fork())
		printf("setenv CLOCKPID %d ; echo clock...", p), exit(0);
	    ...print a clock on the status line every once in a while...
	}

You can now add the pid of the clock "daemon" by doing something
like this:
	CSH			SH			EITHER
	clock >/tmp/$$		clock >/tmp/$$		eval `clock`
	source /tmp/$$		. /tmp/$$
	rm /tmp/$$		rm /tmp/$$

Note that if your environment variables contain shell meta-characters,
you might have problems (only CSH, I think).

--
Rich $alz	{mit-eddie, ihnp4!inmet, wjh12, cca, datacube} !mirror!rs
Mirror Systems	2067 Massachusetts Ave.
617-661-0777	Cambridge, MA, 02140

jpn@teddy.UUCP (09/27/85)

> Is it possible to set a csh environment variable within a C program?

In all the replies to this question, I have not yet seen my favorite techniqe.
This only works on BSD 4.X (at least as far as I know).  There is an
undocumented ioctl() which allows you to push data back onto your input queue
(i.e. simulate characters typed at the terminal).  Using this technique, one
can stuff strings like "setenv TERM xxx\n" into the parent shell's input.

A fragment of the code that does the work:

#include <sgtty.h>

eatthis(string)
register char *string;
    {
    int pendin = LPENDIN;

    noecho();				/* turn off echo mode */
    while (*string)
        {
        ioctl(0, TIOCSTI, string);	/* do the work */
        ++string;
        }
    echo();				/* turn echo back on */
    ioctl(0, TIOCLBIS, &pendin);	/* set the pending input flag */
    }

tmb@talcott.UUCP (Thomas M. Breuel) (09/29/85)

In article <1355@teddy.UUCP>, jpn@teddy.UUCP writes:
> > Is it possible to set a csh environment variable within a C program?
> 
> In all the replies to this question, I have not yet seen my favorite techniqe
> This only works on BSD 4.X (at least as far as I know).  There is an
> undocumented ioctl() which allows you to push data back onto your input queue
> (i.e. simulate characters typed at the terminal).  Using this technique, one
> can stuff strings like "setenv TERM xxx\n" into the parent shell's input.

The ioctl is documented in tty(4). Generally, your program is unlikely to 
be smart enough to type at whatever I invoke it from (e.g. shell escape from 
ed). Use of this ioctl can cause extremely weird behaviour of programs and
shell scripts. Since it is also non-portable, works only on ttys
and might be abolished altogether, I would strongly recommend *not* to use it.

						Thomas.

pdg@ihdev.UUCP (P. D. Guthrie) (09/30/85)

>I don't know why `prog` by itself on a line doesn't work. Probably
>because `...` only returns one word. Oh well...
>
>	jim		cottrell@nbs
>*/
>------

Because "eval `prog`" exists.

pdg@ihdev.UUCP (P. D. Guthrie) (09/30/85)

In article <1355@teddy.UUCP> jpn@teddy.UUCP (John P. Nelson) writes:
>> Is it possible to set a csh environment variable within a C program?
>
>In all the replies to this question, I have not yet seen my favorite techniqe.
>This only works on BSD 4.X (at least as far as I know).  There is an
>undocumented ioctl() which allows you to push data back onto your input queue
>(i.e. simulate characters typed at the terminal).  Using this technique, one
>can stuff strings like "setenv TERM xxx\n" into the parent shell's input.
>
>A fragment of the code that does the work:
>
>#include <sgtty.h>
>
>eatthis(string)
>register char *string;
>    {
>    int pendin = LPENDIN;
>
>    noecho();				/* turn off echo mode */
>    while (*string)
>        {
>        ioctl(0, TIOCSTI, string);	/* do the work */
>        ++string;
>        }
>    echo();				/* turn echo back on */
>    ioctl(0, TIOCLBIS, &pendin);	/* set the pending input flag */
>    }

Undocumented? TTY(4) page 12 (66lines/page).  This call does not exist
on either System 5 or Version 7 - however the hack to put them into the
kernel is about twenty lines.  Oh for this and FIONREAD to be put into
standard SYS V!!!

					Paul Guthrie.

chris@umcp-cs.UUCP (Chris Torek) (10/02/85)

TIOCSTI works, but is unweildy and not very general.  In addition,
you must be careful not to overflow the TTY input queue.  Here is
a program I wrote long ago for saving and restoring C shell history
in, well, an `unusual fasion' which works around the latter problem.

#include <stdio.h>
#include <sgtty.h>
#include <ctype.h>

main(argc, argv)
	int argc;
	register char **argv;
{
	register char *hp, *cp;
	register FILE *in;
	register total = 0;
	static char histbuf[BUFSIZ], cmd[BUFSIZ + 5] = "!#:p ";
	struct sgttyb tty, otty;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s file\n", argv[0]);
		exit(1);
	}
	if ((in = fopen(argv[1], "r")) == NULL) {
		fprintf(stderr, "%s: cannot open %s\n", argv[0], argv[1]);
		exit(1);
	}
	if (ioctl(0, TIOCGETP, &tty) < 0) {
		fprintf(stderr, "%s: not connected to a tty\n", argv[0]);
		exit(1);
	}
	switch (fork()) {
	case -1:
		fprintf(stderr, "%s: fork failed\n", argv[0]);
		exit(1);
	case 0:
		break;
	default:
		exit(0);
	}
	otty = tty;
	tty.sg_flags &= ~ECHO;
	(void) ioctl(0, TIOCSETN, &tty);
	while (fgets(histbuf, sizeof histbuf, in)) {
		if (cmd[5]) {
			for (cp = cmd; *cp; cp++)
				(void) ioctl(0, TIOCSTI, cp);
			if ((total += cp - cmd) >= 100) {/* fudge factor */
				sleep(1);
				total = 0;
			}
		}
		cp = cmd + 5;
		hp = histbuf;
		if (hp[6] == '\t' && isdigit(hp[5]))
			hp += 7;
		while (*cp++ = *hp++);
	}
	/*
	 * Note: drop last command as it was the one that dumped the history
	 * list.
	 */
	(void) ioctl(0, TIOCSETN, &otty);
	exit(0);
}
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

mikel@codas.UUCP (Mikel Manitius) (10/06/85)

> /*
> > Is it possible to set a csh environment variable within a C program? I know
> > about getenv(), but have failed at attempts to set an environment variable
> > from a C program. Basically, what I want to do is "source" a new environment
> > variable for the current csh process. Any comments or suggestions would be
> > appreciated. Thanks.
> 
> In a word NO! There is no way to affect your parent's environment unless
> they made prior arrangements (like before you were born) to do so. 
> Sounds kinda like Life, doesn't it?
>
>		[ ... examples of kludges deleted ... ]

Yet Another Way Neglected (kind of like YACC, but YAWN :-}):

	eval `prog`

	have "prog" generate the following output:

	set home=/usr/mikel
	setenv FOOBAR dodo

	For further usage of eval, consult your manuals.
-- 
                                        =======
     Mikel Manitius                   ==----=====    AT&T
     (305) 869-2462 RNX: 755         ==------=====   Information Systems 
     ...{akguc|ihnp4}!codas!mikel    ===----======   SDSS Regional Support
     ...attmail!mmanitius             ===========    Altamonte Springs, FL
     My opinions are my own.            =======

mouse@mcgill-vision.UUCP (der Mouse) (10/09/85)

> >> Is it possible to set a csh environment variable within a C program?
> >
> >In all the replies to this question, I have not yet seen my favorite techniqe.
> >This only works on BSD 4.X (at least as far as I know).  There is an
> >undocumented ioctl() which allows you to push data back onto your input queue
> >(i.e. simulate characters typed at the terminal).  Using this technique, one
> >can stuff strings like "setenv TERM xxx\n" into the parent shell's input.
> >
[[[ code using TIOCSTI omitted ]]]
>
> Undocumented? TTY(4) page 12 (66lines/page).  This call does not exist
> on either System 5 or Version 7 - however the hack to put them into the
> kernel is about twenty lines.  Oh for this and FIONREAD to be put into
> standard SYS V!!!

     How right you are.  TIOCSTI is one of  the neatest features around.
(Maybe not heavily used, but neat.)  However, I must take issue with

> > Using this technique, one can stuff strings like "setenv TERM xxx\n"
> > into the parent shell's input.

     You can't, in general.  All you can do is stuff stuff back into the
terminal  input stream, which is not always  the  shell's  input  stream
(shell scripts etc).  Besides, guess what it can do to typeahead:

	login: mouse
	Password:

	Last login etc....
	/etc/motd etc....

	cd foo
	em
	% % setenv TEaRM xxx
	emsetenv: Command not found.
	% cs test.c
	cs: Command not found.

     Well, maybe I notice in time not to hit the last newline.  Besides,
that isn't  what  it looks like because I hacked  upon my  shell to give
supersmart line editing (and I don't use "%" for my prompt).

     Tsk  tsk.    Please  try  to  be  more  precise  in  the  future.  
<admonishing frown, and in case someone didn't notice, (:-)>
-- 
					der Mouse

{ihnp4,decvax,akgua,etc}!utcsri!mcgill-vision!mouse
philabs!micomvax!musocs!mcgill-vision!mouse

Hacker: One responsible for destroying /
Wizard: One responsible for recovering it afterward