Keep user env variables executing gksu - c

I have a program in c/gtk which is opened with gksu. The problem is that when I get the environment variable $HOME with getenv("HOME") it returns "root" obviously. I would like to know if there is a way to know who was the user that executed the gksu or the way to get his environmental variables.
Thanks in advance!

See the man page. Use gksu -k command... to preserve the environment (in particular, PATH and HOME).
Or, like Lewis Richard Phillip C indicated, you can use gksu env PATH="$PATH" HOME="$HOME" command... to reset the environment variables for the command. (The logic is that the parent shell, the one run with user privileges, substitutes the variables, and env re-sets them when superuser privileges have been attained.)
If your application should only be run with root privileges, you can write a launcher script -- just like many other applications do. The script itself is basically
#!/bin/sh
exec gksu -k /path/to/your/application "$#"
or
#!/bin/sh
exec gksu env PATH="$PATH" HOME="$HOME" /path/to/your/application "$#"
The script is installed in /usr/bin, and your application as /usr/bin/yourapp-bin or /usr/lib/yourapp/yourapp. The exec means that the command replaces the shell; i.e. nothing after the exec command will ever be executed (unless the application or command cannot be executed at all) -- and most importantly, there will not be an extra shell in memory while your application is begin executed.
While Linux and other POSIX-like systems do have a notion of effective identity (defining the operations an application may do) and real identity (defining the user that is doing the operation), gksu modifies all identities. In particular, while getuid() returns the real user ID for example for set-UID binaries, it will return zero ("root") when gksu is used.
Therefore, the above launch script is the recommended method to solve your problem. It is also a common one; run
file -L /usr/bin/* /usr/sbin/* | sed -ne '/shell/ s|:.*$||p' | xargs -r grep -lie launcher -e '^exec /'
to see which commands are (or declare themselves to be) launcher scripts on your system, or
file -L /bin/* /sbin/* /usr/bin/* /usr/sbin/* | sed -ne '/shell/ s|:.*$||p' | xargs -r grep -lie gksu
to see which ones use gksu explicitly. There is no harm in adopting a known good approach.. ;)

you could assign the values of these environment vars to standard variables, then execute gksu exporting the vars after gkSU... By defining these after the gkSU using && to bind together your executions, so that you essentially execute using cloned environment variables...
A better question is why do this at all? I realize you are wanting to keep some folders, but am not sure why as any files created as root, would have to be globally writable, probably using umask, or you would have to manually revise permissions or change ownership... This is such a bad Idea!
Please check out https://superuser.com/questions/232231/how-do-i-make-sudo-preserve-my-environment-variables , http://www.cyberciti.biz/faq/linux-unix-shell-export-command/ & https://serverfault.com/questions/62178/how-to-specify-roots-environment-variable

Related

manipulating strings and use commands with the result in bash [duplicate]

I'm trying to write a small script to change the current directory to my project directory:
#!/bin/bash
cd /home/tree/projects/java
I saved this file as proj, added execute permission with chmod, and copied it to /usr/bin. When I call it by:
proj, it does nothing. What am I doing wrong?
Shell scripts are run inside a subshell, and each subshell has its own concept of what the current directory is. The cd succeeds, but as soon as the subshell exits, you're back in the interactive shell and nothing ever changed there.
One way to get around this is to use an alias instead:
alias proj="cd /home/tree/projects/java"
You're doing nothing wrong! You've changed the directory, but only within the subshell that runs the script.
You can run the script in your current process with the "dot" command:
. proj
But I'd prefer Greg's suggestion to use an alias in this simple case.
The cd in your script technically worked as it changed the directory of the shell that ran the script, but that was a separate process forked from your interactive shell.
A Posix-compatible way to solve this problem is to define a shell procedure rather than a shell-invoked command script.
jhome () {
cd /home/tree/projects/java
}
You can just type this in or put it in one of the various shell startup files.
The cd is done within the script's shell. When the script ends, that shell exits, and then you are left in the directory you were. "Source" the script, don't run it. Instead of:
./myscript.sh
do
. ./myscript.sh
(Notice the dot and space before the script name.)
To make a bash script that will cd to a select directory :
Create the script file
#!/bin/sh
# file : /scripts/cdjava
#
cd /home/askgelal/projects/java
Then create an alias in your startup file.
#!/bin/sh
# file /scripts/mastercode.sh
#
alias cdjava='. /scripts/cdjava'
I created a startup file where I dump all my aliases and custom functions.
Then I source this file into my .bashrc to have it set on each boot.
For example, create a master aliases/functions file: /scripts/mastercode.sh
(Put the alias in this file.)
Then at the end of your .bashrc file:
source /scripts/mastercode.sh
Now its easy to cd to your java directory, just type cdjava and you are there.
You can use . to execute a script in the current shell environment:
. script_name
or alternatively, its more readable but shell specific alias source:
source script_name
This avoids the subshell, and allows any variables or builtins (including cd) to affect the current shell instead.
Jeremy Ruten's idea of using a symlink triggered a thought that hasn't crossed any other answer. Use:
CDPATH=:$HOME/projects
The leading colon is important; it means that if there is a directory 'dir' in the current directory, then 'cd dir' will change to that, rather than hopping off somewhere else. With the value set as shown, you can do:
cd java
and, if there is no sub-directory called java in the current directory, then it will take you directly to $HOME/projects/java - no aliases, no scripts, no dubious execs or dot commands.
My $HOME is /Users/jleffler; my $CDPATH is:
:/Users/jleffler:/Users/jleffler/mail:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work
Use exec bash at the end
A bash script operates on its current environment or on that of its
children, but never on its parent environment.
However, this question often gets asked because one wants to be left at a (new) bash prompt in a certain directory after execution of a bash script from within another directory.
If this is the case, simply execute a child bash instance at the end of the script:
#!/usr/bin/env bash
cd /home/tree/projects/java
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
exec bash
To return to the previous, parental bash instance, use Ctrl+D.
Update
At least with newer versions of bash, the exec on the last line is no longer required. Furthermore, the script could be made to work with whatever preferred shell by using the $SHELL environment variable. This then gives:
#!/usr/bin/env bash
cd desired/directory
echo -e '\nHit [Ctrl]+[D] to exit this child shell.'
$SHELL
I got my code to work by using. <your file name>
./<your file name> dose not work because it doesn't change your directory in the terminal it just changes the directory specific to that script.
Here is my program
#!/bin/bash
echo "Taking you to eclipse's workspace."
cd /Developer/Java/workspace
Here is my terminal
nova:~ Kael$
nova:~ Kael$ . workspace.sh
Taking you to eclipe's workspace.
nova:workspace Kael$
simply run:
cd /home/xxx/yyy && command_you_want
When you fire a shell script, it runs a new instance of that shell (/bin/bash). Thus, your script just fires up a shell, changes the directory and exits. Put another way, cd (and other such commands) within a shell script do not affect nor have access to the shell from which they were launched.
You can do following:
#!/bin/bash
cd /your/project/directory
# start another shell and replacing the current
exec /bin/bash
EDIT: This could be 'dotted' as well, to prevent creation of subsequent shells.
Example:
. ./previous_script (with or without the first line)
On my particular case i needed too many times to change for the same directory.
So on my .bashrc (I use ubuntu) i've added the
1 -
$ nano ~./bashrc
function switchp
{
cd /home/tree/projects/$1
}
2-
$ source ~/.bashrc
3 -
$ switchp java
Directly it will do: cd /home/tree/projects/java
Hope that helps!
It only changes the directory for the script itself, while your current directory stays the same.
You might want to use a symbolic link instead. It allows you to make a "shortcut" to a file or directory, so you'd only have to type something like cd my-project.
You can combine Adam & Greg's alias and dot approaches to make something that can be more dynamic—
alias project=". project"
Now running the project alias will execute the project script in the current shell as opposed to the subshell.
You can combine an alias and a script,
alias proj="cd \`/usr/bin/proj !*\`"
provided that the script echos the destination path. Note that those are backticks surrounding the script name.
For example, your script could be
#!/bin/bash
echo /home/askgelal/projects/java/$1
The advantage with this technique is that the script could take any number of command line parameters and emit different destinations calculated by possibly complex logic.
to navigate directories quicky, there's $CDPATH, cdargs, and ways to generate aliases automatically
http://jackndempsey.blogspot.com/2008/07/cdargs.html
http://muness.blogspot.com/2008/06/lazy-bash-cd-aliaes.html
https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5827311.html
In your ~/.bash_profile file. add the next function
move_me() {
cd ~/path/to/dest
}
Restart terminal and you can type
move_me
and you will be moved to the destination folder.
You can use the operator && :
cd myDirectory && ls
While sourcing the script you want to run is one solution, you should be aware that this script then can directly modify the environment of your current shell. Also it is not possible to pass arguments anymore.
Another way to do, is to implement your script as a function in bash.
function cdbm() {
cd whereever_you_want_to_go
echo "Arguments to the functions were $1, $2, ..."
}
This technique is used by autojump: http://github.com/joelthelion/autojump/wiki to provide you with learning shell directory bookmarks.
You can create a function like below in your .bash_profile and it will work smoothly.
The following function takes an optional parameter which is a project.
For example, you can just run
cdproj
or
cdproj project_name
Here is the function definition.
cdproj(){
dir=/Users/yourname/projects
if [ "$1" ]; then
cd "${dir}/${1}"
else
cd "${dir}"
fi
}
Dont forget to source your .bash_profile
This should do what you want. Change to the directory of interest (from within the script), and then spawn a new bash shell.
#!/bin/bash
# saved as mov_dir.sh
cd ~/mt/v3/rt_linux-rt-tools/
bash
If you run this, it will take you to the directory of interest and when you exit it it will bring you back to the original place.
root#intel-corei7-64:~# ./mov_dir.sh
root#intel-corei7-64:~/mt/v3/rt_linux-rt-tools# exit
root#intel-corei7-64:~#
This will even take you to back to your original directory when you exit (CTRL+d)
I did the following:
create a file called case
paste the following in the file:
#!/bin/sh
cd /home/"$1"
save it and then:
chmod +x case
I also created an alias in my .bashrc:
alias disk='cd /home/; . case'
now when I type:
case 12345
essentially I am typing:
cd /home/12345
You can type any folder after 'case':
case 12
case 15
case 17
which is like typing:
cd /home/12
cd /home/15
cd /home/17
respectively
In my case the path is much longer - these guys summed it up with the ~ info earlier.
As explained on the other answers, you have changed the directory, but only within the sub-shell that runs the script. this does not impact the parent shell.
One solution is to use bash functions instead of a bash script (sh); by placing your bash script code into a function. That makes the function available as a command and then, this will be executed without a child process and thus any cd command will impact the caller shell.
Bash functions :
One feature of the bash profile is to store custom functions that can be run in the terminal or in bash scripts the same way you run application/commands this also could be used as a shortcut for long commands.
To make your function efficient system widely you will need to copy your function at the end of several files
/home/user/.bashrc
/home/user/.bash_profile
/root/.bashrc
/root/.bash_profile
You can sudo kwrite /home/user/.bashrc /home/user/.bash_profile /root/.bashrc /root/.bash_profile to edit/create those files quickly
Howto :
Copy your bash script code inside a new function at the end of your bash's profile file and restart your terminal, you can then run cdd or whatever the function you wrote.
Script Example
Making shortcut to cd .. with cdd
cdd() {
cd ..
}
ls shortcut
ll() {
ls -l -h
}
ls shortcut
lll() {
ls -l -h -a
}
If you are using fish as your shell, the best solution is to create a function. As an example, given the original question, you could copy the 4 lines below and paste them into your fish command line:
function proj
cd /home/tree/projects/java
end
funcsave proj
This will create the function and save it for use later. If your project changes, just repeat the process using the new path.
If you prefer, you can manually add the function file by doing the following:
nano ~/.config/fish/functions/proj.fish
and enter the text:
function proj
cd /home/tree/projects/java
end
and finally press ctrl+x to exit and y followed by return to save your changes.
(NOTE: the first method of using funcsave creates the proj.fish file for you).
You need no script, only set the correct option and create an environment variable.
shopt -s cdable_vars
in your ~/.bashrc allows to cd to the content of environment variables.
Create such an environment variable:
export myjava="/home/tree/projects/java"
and you can use:
cd myjava
Other alternatives.
Note the discussion How do I set the working directory of the parent process?
It contains some hackish answers, e.g.
https://stackoverflow.com/a/2375174/755804 (changing the parent process directory via gdb, don't do this) and https://stackoverflow.com/a/51985735/755804 (the command tailcd that injects cd dirname to the input stream of the parent process; well, ideally it should be a part of bash rather than a hack)
It is an old question, but I am really surprised I don't see this trick here
Instead of using cd you can use
export PWD=the/path/you/want
No need to create subshells or use aliases.
Note that it is your responsibility to make sure the/path/you/want exists.
I have to work in tcsh, and I know this is not an elegant solution, but for example, if I had to change folders to a path where one word is different, the whole thing can be done in the alias
a alias_name 'set a = `pwd`; set b = `echo $a | replace "Trees" "Tests"` ; cd $b'
If the path is always fixed, the just
a alias_name2 'cd path/you/always/need'
should work
In the line above, the new folder path is set
This combines the answer by Serge with an unrelated answer by David. It changes the directory, and then instead of forcing a bash shell, it launches the user's default shell. It however requires both getent and /etc/passwd to detect the default shell.
#!/usr/bin/env bash
cd desired/directory
USER_SHELL=$(getent passwd <USER> | cut -d : -f 7)
$USER_SHELL
Of course this still has the same deficiency of creating a nested shell.

Where to store settings for C command line apps and bash scripts?

This may be an obvious question but I can't find a definitive answer.
When making a command line utility in C or when writing bash scrips where can I save values for later reference?
What I'm looking for is something similar to NSUserDefaults.
For the bash setup, the shell invocation normally reads /etc/profile, and the private equivalent ~/.bash_profile or ~/.bashrc, upon startup. So look at these files and make the appropriate modifications. If possible, I suggest making a backup of these files prior to making any changes.
Be aware that the /etc/profile file will generally provide global settings while, if an equivalent file exists in your home directory, that file may override the global settings.
If you wish to add or modify environment variables on the fly, try ...
a. adding the following code to the end of your ~/.bash_profile or ~/.bashrc file
if [ -e ./.bashadd ]
then
source ./.bashadd
fi
b. append additions or modifications to the file ./.bashadd on the fly (NOTE: you'll have to handle this within your program)
echo export NAME=John >> ./.bashadd
c. at login, when you invoke bash or when you source your ~/.bash_profile or ~/.bashrc file, the environment variables will be available
Test:
[shell ~]$ echo export NAME=John >> ./.bashadd
[shell ~]$ source ./.bashrc
[shell ~]$ echo $NAME
John
[shell ~]$
Admittedly, not an ideal solution. And I would suggest doing this only in your local environment and not globally (i.e. not with /etc/profile)

Run C program from shell script [duplicate]

I have a script in unix that looks like this:
#!/bin/bash
gcc -osign sign.c
./sign < /usr/share/dict/words | sort | squash > out
Whenever I try to run this script it gives me an error saying that squash is not a valid command. squash is a shell script stored in the same directory as this script and looks like this:
#!/bin/bash
awk -f squash.awk
I have execute permissions set correctly but for some reason it doesn't run. Is there something else I have to do to make it able to run like shown? I am rather new to scripting so any help would be greatly appreciated!
As mentioned in #Biffen's comment, unless . is in your $PATH variable, you need to specify ./squash for the same reason you need to specify ./sign.
When parsing a bare word on the command line, bash checks all the directories listed in $PATH to see if said word is an executable file living inside any of them. Unless . is in $PATH, bash won't find squash.
To avoid this problem, you can tell bash not to go looking for squash by giving bash the complete path to it, namely ./squash.

Use of echo and system to run a software in C

I am trying to run a biological program called BLASTP which takes in two strings (fasta_GWIDD and fasta_UNIPROT in the code) and compares them. The problem that I am encountering is the use of echo/system in the code. Can anyone suggest what am I missing out??
for(i=0;i<index1;i++)
{
sprintf(fasta_GWIDD,">%s\\n%s\n",fasta_name1[i],fasta_seq1[i]);
setenv("GwiddVar", fasta_GWIDD, 1) ;
sprintf(fasta_UNIPROT,">%s\\n%s\n",fasta_name2[i],fasta_seq2[i]);
setenv("UniprotVar", fasta_UNIPROT, 1) ;
system("blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)");
}
The error is:
sh: -c: line 0: syntax error near unexpected token `('
sh: -c: line 0: `blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)'
It seems that the shell does not understand the
<(echo -e $GwiddVar)
syntax. Mind that the system command may use different shell than the one you are used to (like csh instead of bash, and so on). It's everything in somewhere in your OS config files and profile, but I can't guess what you have out there.
Btw. I think that you should be able to check which shell is being used by the system() command by either of these:
system("echo $SHELL") // should simply write the path to current shell
system("ps -aux") // look at it and find what is the parent of the PS
etc.
Considering that this was correct on some shell:
blastp -query <(echo -e $GwiddVar) -subject<(echo -e $UniprotVar)
The syntax cited above apparently is meant only to pass the variable as intput. I think you are overdoing it. You are using echo -e $GwiddVar to print and capture the data, which you already have in a vairable at hand. Have you tried something as simple as:
blastp -query $GwiddVar -subject $UniprotVar
I don't know which shell you are trying to use, but considering that echo got its data, then it should be exactly the same.
If you are worried about spaces, then various shells usually allow you to use quotation marks:
blastp -query "$GwiddVar" -subject "$UniprotVar"
Of course it depends on the shell. If your program uses a shell that does not like quotation marks, well, you have to adapt it. Not to your shell, but to the shell the system() has used.
Another thing is that using system is quite rough. When you have arguments that are difficult to escape correctly, you should be using other functions like execve that are able to take an array of real raw direct strings and pass them directly as ARGV to the process. Using these, you will not need (and you should not) add any quotes or escape any spaces in the strings to be passed.
sprintf(fasta_GWIDD,">%s\\n%s\n",fasta_name1[i],fasta_seq1[i]);
sprintf(fasta_UNIPROT,">%s\\n%s\n",fasta_name2[i],fasta_seq2[i]);
char** args = .....; // allocate an array of char*[5], malloc, or whatever
args[0] = "blastp";
args[1] = "-query";
args[2] = fasta_GWIDD;
args[3] = "-subject";
args[4] = fasta_UNIPROT;
int errcode = execve(4, args, null);
if( errcode ) ... // check the error (if any) and react
However! Note that the execve comes from the exec family, so it replaces your current process. This is why I write only a sketch and don't show the whole ready-to-run code. You will probably need to fork() before it and then wait for the children in the outer loop.
So, I'd first check the shell and syntax ;)
From man 3 system:
DESCRIPTION
system() executes a command specified in command by calling /bin/sh -c
command, and returns after the command has been completed.
On many systems, /bin/sh is not bash, and even when it is, it is a different configuration of bash (bash typically operates differently if it is invoked as /bin/sh). So, you are passing bash syntax to a shell that is either not bash, or doesn't allow the full set of bash-isms... Also, there's a space missing after -system that might be confusing things as well... And, I'm not entirely sure environment variables are expanded within system() strings...

How to create file and put string on it using shellscript?

I want to create a file in /usr/share/applications/ and put a string on it.
What I have so far:
sudo touch /usr/share/applications/test.desktop
dentry="testing"
sudo echo $dentry >> /usr/share/applications/test.desktop
But this raise an error Permission Denied. What should I do to make it works?
You should create the file using your own pernissions, then sudo cp it into place.
The reason the second command doesn't work is that the redirection is set up by your shell, before sudo even runs. You could work around this by running sudo sh -c 'echo stuff >>file' but this is vastly more risk-prone than a simple sudo cp, and additionally has a race condition (if you run two concurrent instances of this script, they could end up writing the information twice to the file).

Resources