George Mauer <gma...@gmail.com> writes:
> I do still wonder what would be the disadvantage of just configuring it to do > --login by default and doing all configuration in profile scripts. It would be > unconventional yes, but it would also make dynamic scoping of environment > variable effectively opt-in via --noprofile rather than opt-out (which imo is > how it should be). I would assume that uses extra resources or risks > improperly handling crashed processes, but I can't find anything to that > effect in the > docs The following is long, but may provide some of the context you are looking for to help understand all the moving parts involved here. Some of ti is not 100% accurate, but trying to be 100% accurate would make it longer and would not necessarily help with the mental model needed to understand how this all fits together. Hopefully, this has a reasonable balance between accuracy and simplicity! While there is certainly aspects of shell architecture which are less relevant in a modern computing environment, the way it all works is actually pretty good. It can sometimes help to remember what early Unix environments were like and what the resource constraints were to understand some of the design decisions. A time when memory and disk storage was extremely expensive and in short supply, where CPUs were slower and less capable than those commonly found in a modern washing machine. This was a time where every CPU cycle, every memory bit and every byte of storage had to be justified and used efficiently. The 'login' shell actually has a special purpose and a few subtleties associated with it that may not be obvious at first. For example, the login shell usually records a user's login in a few system files like wtmp and utmp. The idea is that the login shell will be the 'root' of a user's process tree. One of its key roles is to setup the environment which will be inherited by all the other (child) processes which the user initiates. This includes some things which you only want run once and processes which must run as parents to all other processes. A good example is setting up an ssh agent and adding ssh keys to that agent. You only want this to occur once, so you might add this to your .profile (or whatever the shell equivalent is, such as .bash_profile or .zsh_profile). More critical is the requirement that the ssh agent must be run inside a parent process for child processes to be able to use it. This means it needs to be near the 'root' login process to ensure all children are able to take advantage of the service it provides. (Keep in mind that some platforms, like the mac, have created a whole new architecture for managing keys etc and even on Linux, it is rarely necessary to run ssh-agent like we did in the 'old days' as that functionality has been subsumed into desktop environments like gnome) It is also here that you will define static environment variables i.e. those which will not change during the lifetime of the session and which are exported, meaning they are made available in the environments of child processes. Examples are setting up things like PATH, EMAIL etc. Some common environment variables, like USER, HOME etc are done automatically as part of the system wide profile in /etc/profile or the system wide path defined in /etc/paths and /etc/paths.d etc. In addition to these static environment variables are more dynamic ones which change and therefore need to be updated with each new shell process. This can include variables like PWD and CWD which track the parent and current working directory or the PS1 and PS2 variables which contain details about your shell prompt that may include dynamic information such as the current working directory, current date/time or status of the git repository associated with the current working directory. This is essentially the role fulfilled by the 'rc' files, like .bashrc. As these dynamic enironment variables tend to only be relevant for interactive shells, the rc file is only 'sourced' for those shells. When you execute a command, it creates a new process which has an execution environment associated with it (there are some exceptions, such as built-in shell commands that run inside the current process and command 'modifiers' like 'exec' which replace the current process with the command being executed). Some of these processes will be very short lived, executing a single command or possibly run for hours, days, weeks (such as Emacs). This process is a child process of the process which ran/executed the command. You can use the -f (forest) switch to ps to see the relationship between processes. Those processes in turn may execute more child processes (such as when Emacs creates a shell process to perform some task, like run a compilation task, spawn a shell or terminal, etc. The environment each of these processes inherits consists of the exported variables defined in the parent process. This is typically all the exported variables from the login process (as it is the parent of all) and any variables exported by parent processes in the process hierarchy (such as any exported as part of an interactive shell which sourced .bashrc for example) or is defined and exported in a parent (such as part of a shell script which then executes the command which creates the child process. So, in general, you don't want to run all or many of your processes as a login shell. At the very least it is wasting resources and at the worst it could actually cause confusion or break things because it is causing things to be run multiple times which should only be run once. I suspect some of the confusion here is being cause because of the different model approach used by the Mac OS. On the mac, you actually have at least two process trees (I'm not a mac expert, so it is possible there are more than two - in fact I'm pretty sure there are, but for explanation purposes....). On the mac you have your login process tree and your 'dock' (launcher) process tree. The dock process tree is not rooted as the child of a login shell, so there is no sourcing of .profile and it is not an interactive shell, so no .bashrc either. The execution environment associated with processes it spawns (such as when you run a command) only consist of the global system environment setup by the system prfile in /etc/profile, /etc/paths and /etc/paths.d and any environment variables created and exported by the process itself (i.e. using the setenv command in your emacs init.el). Whether emacs is run in terminal mode or in GUI mode has nothing to do with what environment context it has. The context is determined by the environment context of the process parent. So a common way to ensure your .profile environment variables are available in Emacs when it is run from the dock is to use a shell wrapper script which first sources .profile to create and export the variables in .pofile and then run emacs. Often you won't just make it a login shell, but simply source .profile. It is this script which is added to the dock and is executed to start Emacs, ensuring it has your .profile variables set and exported to the child process, Emacs. In turn, any child processes created by Emacs will also include this environment context (such as wehn you open an emacs terminal, run M-x shell or execute org source blocks). The other approach is to just add the variables you need using 'setenv' i your emacs init.el file. Often, there are only a few additional variables you want inside of Emacs and child processes it creates, so adding them to your init.el file is not hard. The one which is most often needed is updates to the PATH variable to add additional directories to search for executable files. You can use setenv to do this or you can use the exec-path package or you can do what I do, which is add these additional paths to /etc/paths.d. I don't bother with the wrapper script on the Mac. There are only a couple of additional variables I need defined, so I set them in my init.el using setenv. -- Tim Cross