You are not logged in.

#1 2013-01-15 20:33:16

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

[SOLVED] Using a variable from getcwd() in execl()

Hey all,

I'm writing my very first graphical application (in GTK3) using C. It's supposed to be a launcher for two of my scripts, found here: https://github.com/Unia/runescape-client (yes, I play it)

So, it's a GTK3 window containing two buttons (each to launch a script) and a webkit view showing the latest Runescape news. Now, I need to find the path of the script (accomplished with getcwd) and then glue that together with the name of the script to launch.

Here's the function that gets executed upon pressing the button to update the client:

static void
update_client (GtkButton* button)
{
	char cwd[1024];
	getcwd(cwd, sizeof(cwd));
	execl("runescape-update-client", NULL);
}

Now, my question is how to incorporate cwd into the path in execl(). Is this possible? Is there perhaps a better way of accomplishing what I want?

Thanks in advance!

EDIT: To be more clear, this is what I'd want to do:

	execl("/output/of/cwd/runescape-update-client", NULL);

Last edited by Unia (2013-01-16 19:42:10)


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#2 2013-01-15 21:52:12

moetunes
Member
From: A comfortable couch
Registered: 2010-10-09
Posts: 1,021

Re: [SOLVED] Using a variable from getcwd() in execl()

man execl wrote:

int execl(const char *path, const char *arg, ...)

since execl just needs a char a simple method is :

    char cwd[1024];
    getcwd(cwd, sizeof(cwd));
    strcat(cwd, "runescape-update-client");
    execl(cwd, NULL);

I'm sure there are lots of other methods for doing the same thing.


You're just jealous because the voices only talk to me.

Offline

#3 2013-01-15 21:53:20

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

^ Nice, I didn't think of that one. For now I'm going with that one, but in the meantime - are there other suggestions?

Thanks moetunes!

EDIT: It works, of course.

However, instead of showing the directory of the binary (which resides in /opt/runescape) I get the directory from where I run the script (my homedir). Is my way of handling getcwd wrong?

Last edited by Unia (2013-01-15 22:06:29)


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#4 2013-01-15 22:10:21

Trilby
Forum Moderator
From: Massachusetts, USA
Registered: 2011-11-29
Posts: 13,843
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

I'm confused, does the script move?  Is it in a different location each time this runs?  If not, why do you need to dynamically determine its location?  And if it is always in the current working directory and it is just a shell script, you could use system() instead of execl().

If it does need to be dynamically determined at run time, then moetune's suggestion is exactly what I'd do.


InterrobangSlider
• How's my coding? See this page.
• How's my moderating? Feel free to email any concerns, complaints, or objections.

Offline

#5 2013-01-15 22:13:47

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Trilby wrote:

I'm confused, does the script move?  Is it in a different location each time this runs?  If not, why do you need to dynamically determine its location?  And if it is always in the current working directory and it is just a shell script, you could use system() instead of execl().

It does not move, it has a fixed location inside /opt/runescape (symlink pointing to /usr/bin) but hardcoding its path doesn't seem the right way to do it. Or is it?

Trilby wrote:

If it does need to be dynamically determined at run time, then moetune's suggestion is exactly what I'd do.

I would want to go this way, but getcwd() returns the directory from which I launch it. It does not return the actual location of the binary. Am I doing something wrong?

EDIT: Also, I use execl() because I want the chosen script to replace the launcher. When I use system(), the launcher stays open.

Last edited by Unia (2013-01-15 22:37:58)


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#6 2013-01-15 22:46:13

moetunes
Member
From: A comfortable couch
Registered: 2010-10-09
Posts: 1,021

Re: [SOLVED] Using a variable from getcwd() in execl()

There's nothing wrong with hardcoding the path.


You're just jealous because the voices only talk to me.

Offline

#7 2013-01-15 22:49:58

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

moetunes wrote:

There's nothing wrong with hardcoding the path.

But I can't image other, more like "official" programs, hardcode paths too? I can go for it, I guess, but I'm also curious why getcwd doesn't do what I read it should do.


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#8 2013-01-15 22:59:56

Trilby
Forum Moderator
From: Massachusetts, USA
Registered: 2011-11-29
Posts: 13,843
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Getcwd gets the current working directory ...  So of course it will not give you the path to the script.

Oh ... I see - you were thinking it'd get the directory that the program resides in?  That makes some sense, but that is not the way it works, nor should it.  Users should be able to move a program without breaking it.  And running programs don't know where there binary files are stored.  Technically they don't need to be stored - a program can keep running after it has been removed from disk.  This actually happens whenever you update your kernel.

There is nothing at all wrong with hardcoding a path.  But you can add various levels of flexibility by doing one of the following:
1) define the path (and/or the full command) as a constant

static const char *udpate_script = "/path/to/update-script.sh";

2) use an actual variable rather than a constant

static const char *default_path = "/default/path/to/script/";
static char *path;
static const char *updatecmd = "scriptname.sh";

static void update_client(CrappyToolkitType *Widget) {
   char cmd[MAX_CMD+1];
   strcpy(cmd,path);
   strcat(cmd,updatecmd);
   execl(cmd,NULL);
}

int main(int argc,const char **argv) {
   path = default_path;
   /* some code to read an optional command line parameters that finds argv[i] to hold a new path */
   path = argv[i];
...

3) only here because lists look better with three...

Last edited by Trilby (2013-01-15 23:03:48)


InterrobangSlider
• How's my coding? See this page.
• How's my moderating? Feel free to email any concerns, complaints, or objections.

Offline

#9 2013-01-15 23:08:31

Trilby
Forum Moderator
From: Massachusetts, USA
Registered: 2011-11-29
Posts: 13,843
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

As a totally different approach, much closer to your original intent, you could extract the path from argv[0] pretty easily.  And if you *know* that the scripts will be installed in the same directory as the binary, then this would do what you intend.  I would not, however, recommend this approach.  As soon as someone tries to make a symlink to the binary, it will stop working.


InterrobangSlider
• How's my coding? See this page.
• How's my moderating? Feel free to email any concerns, complaints, or objections.

Offline

#10 2013-01-15 23:11:46

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Thanks Trilby! I will look into it tomorrow (its already 0.16 now here).


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#11 2013-01-16 00:45:30

BennyBolton
Member
From: Christchurch New Zealand
Registered: 2011-10-14
Posts: 63
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

If you want to get the path of the running binary then use readlink to read /proc/self/exe

char buf[1024],*str;
ssize_t len;
if((len=readlink("/proc/self/exe",buf,sizeof(buf)-1))!=-1){
    buf[len]=0;
    if(str=strrchr(buf,'/')){
        str[1]=0;
    }
    strcat(buf,"runescape-update-client")
    execl(buf, NULL);
}

Edit: I corrected the sample above

Last edited by BennyBolton (2013-01-16 05:17:29)


HP DV6 + Arch + Openbox

Offline

#12 2013-01-16 00:50:51

Trilby
Forum Moderator
From: Massachusetts, USA
Registered: 2011-11-29
Posts: 13,843
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

That will only provide the same thing as argv[0], right?


InterrobangSlider
• How's my coding? See this page.
• How's my moderating? Feel free to email any concerns, complaints, or objections.

Offline

#13 2013-01-16 01:46:10

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 987

Re: [SOLVED] Using a variable from getcwd() in execl()

Yes, but argv[0] doesn't always have a path in it, so readlink'ing /proc/self/exe is a more robust method. Still not great, though -- programs that do different things based on where they are in the filesystem just invite hard-to-find bugs. I'd hard-code a path and use an environment variable or command line option to change it when necessary.

$ foo                                                    # uses "/opt/runescape/runescape-update-client"
$ RUC_DIR=/usr/local/bin foo                             # uses "/usr/local/bin/runescape-update-client"
$ foo --client=/usr/local/bin/runescape-update-client    # another way to do that

Assuming I've understood your intent correctly. There are several ways programs find the files they need, but looking in the directory the binary resides in is probably one of the last ones I'd try.

Last edited by Trent (2013-01-16 01:46:54)

Offline

#14 2013-01-16 05:25:02

BennyBolton
Member
From: Christchurch New Zealand
Registered: 2011-10-14
Posts: 63
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

/proc/self/exe should always point to the binary/script of the running process, even if a symlink to it was launched, unlike depending on argv[0]. If it cannot be known where the binary is (e.g. the user doesn't install it and launch it out of thier home directory), readlink /proc/self/exe should do the trick

Last edited by BennyBolton (2013-01-16 05:27:55)


HP DV6 + Arch + Openbox

Offline

#15 2013-01-16 10:54:49

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Readlink'ing /proc/self/exe was my initial approach, actually. I just didn't know how to remove the binary name from the path it returns so I started looking for alternatives and then found getcwd(), whose purpose I obviously misunderstood big_smile

To clarify, my scripts (both written in perl) do not do anything differently if they are in another location. I just want to be able to use my launcher to launch these script no matter where I put them or from where I run them.

I will try the new readlink suggestion and in the future I might work on getting the path with an argument. Thanks everyone!

EDIT:Yep, the new readlink suggestion works! I do however get two warnings when compiling:

/home/jente/abs/runescape-client/runescape_launcher/src/main.c:51:2: let op: null argument waar een niet-null argument vereist is (argument 2) [-Wnonnull]
/home/jente/abs/runescape-client/runescape_launcher/src/main.c:51:2: let op: not enough variable arguments to fit a sentinel [-Wformat]

I don't know how to get Anjuta to use English as output language, but the first one translates to "null argument where a non-null argument is required (argument 2)"
It does work, so I could ignore the warnings. I'm just curious why they're there. Here's the relevant code:

Fixed by adding a second buf: execl(buf,buf,NULL);

Also, could I move this all to a new function and call that function in this update_client function? I have two of these buttons and I would like to only use this code once instead of in both the button functions.

Last edited by Unia (2013-01-16 11:17:33)


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#16 2013-01-16 19:01:22

BennyBolton
Member
From: Christchurch New Zealand
Registered: 2011-10-14
Posts: 63
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Unia wrote:

Also, could I move this all to a new function and call that function in this update_client function? I have two of these buttons and I would like to only use this code once instead of in both the button functions.

yes

int run_script(const char *name){
    char buf[1024],*str;
    ssize_t len;
    if(!name){
        return 0;
    }
    if((len=readlink("/proc/self/exe",buf,sizeof(buf)-1))!=-1){
        buf[len]=0;
        if(str=strrchr(buf,'/')){
            str[1]=0;
        }
        strcat(buf,name)
        execl(buf,buf,NULL);
    }
    return 1;
}

also, you may like to consider using FILENAME_MAX instead of 1024


HP DV6 + Arch + Openbox

Offline

#17 2013-01-16 19:28:21

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

^ I meant I have a getcurrentpath() function, which I call in both the update_client() function and the launch_client() function. It's working now with this:

int
getcurrentpath(const char *name)
{
	if((len=readlink("/proc/self/exe", buf, sizeof(buf) -1))!=-1){
		buf[len]=0;
		if((str=strrchr(buf,'/')))
			str[1]=0;
	}
	return buf;
}

But when compiling I get this warning on the return buf line:

main.c:36:2: let op: return makes integer from pointer without a cast [enabled by default]

Last edited by Unia (2013-01-16 19:28:47)


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#18 2013-01-16 19:36:08

BennyBolton
Member
From: Christchurch New Zealand
Registered: 2011-10-14
Posts: 63
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

The function is supposed to return a int but your returning a char *, im guessing buf and str are defined globally? try this

char *
getcurrentpath(const char *name)
{
	if((len=readlink("/proc/self/exe", buf, sizeof(buf) -1))!=-1){
		buf[len]=0;
		if((str=strrchr(buf,'/'))){
			str[1]=0;
		} else {
			return NULL;
		}
		return buf;
	}
	return NULL;
}

returns NULL on error

Last edited by BennyBolton (2013-01-16 19:37:08)


HP DV6 + Arch + Openbox

Offline

#19 2013-01-16 19:41:39

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

I knew it had something to do with that. Thanks! Now it's all working just fine smile

Marking solved!


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#20 2013-01-16 20:02:19

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 987

Re: [SOLVED] Using a variable from getcwd() in execl()

Unia wrote:

To clarify, my scripts (both written in perl) do not do anything differently if they are in another location.

But the launcher program itself does.

If it works and you're the only one who uses it, sure, whatever, it's your program. But it's simply not a good approach overall.

Offline

#21 2013-01-16 20:21:44

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Trent wrote:
Unia wrote:

To clarify, my scripts (both written in perl) do not do anything differently if they are in another location.

But the launcher program itself does.

If it works and you're the only one who uses it, sure, whatever, it's your program. But it's simply not a good approach overall.

Can you elaborate on that? As I said this is my first (GUI) program so I'm still learning the ropes.

I don't think my program acts different on its location. I just made it look for both the script in the same directory, no? So it does exactly the same if, say, launched from /home or from /opt


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

#22 2013-01-17 02:26:17

Trilby
Forum Moderator
From: Massachusetts, USA
Registered: 2011-11-29
Posts: 13,843
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

It does the same if launched from /home or /opt *if and only if* those scripts are *also* in the same place.  This is a bad assumption to make.

Why not code the scripts in C too?

(edit: lots of typos for such a short post)

Last edited by Trilby (2013-01-17 12:53:38)


InterrobangSlider
• How's my coding? See this page.
• How's my moderating? Feel free to email any concerns, complaints, or objections.

Offline

#23 2013-01-17 10:38:35

Unia
Member
From: The Netherlands
Registered: 2010-03-30
Posts: 2,471
Website

Re: [SOLVED] Using a variable from getcwd() in execl()

Trilby wrote:

Why not code the scripts in C too?

I was playing around with the idea wink It's going to be alot of work for a beginner like me, but I should learn alot!


If you can't sit by a cozy fire with your code in hand enjoying its simplicity and clarity, it needs more work. --Carlos Torres

Offline

Board footer

Powered by FluxBB