2009-04-28 18 views
18

Linuxの場合、アプリケーションは/proc/self/exeを照会することで簡単に絶対パスを取得できます。 FreeBSDでは、sysctl呼び出しを構築する必要があるので、もっと複雑です。OS Xコマンドラインアプリケーションの絶対パスをプログラムで取得する

int mib[4]; 
mib[0] = CTL_KERN; 
mib[1] = KERN_PROC; 
mib[2] = KERN_PROC_PATHNAME; 
mib[3] = -1; 
char buf[1024]; 
size_t cb = sizeof(buf); 
sysctl(mib, 4, buf, &cb, NULL, 0); 

しかし、まだ完全に実行可能です。しかし、私はコマンドラインアプリケーションのためにこれをOS Xで判断する方法を見つけることができません。アプリケーションバンドルから実行している場合は、[[NSBundle mainBundle] bundlePath]を実行して確認できますが、コマンドラインアプリケーションはバンドルに含まれていないため、これは役に立ちません。

(注、シンボリックリンクから起動した場合、argv[0]はそのシンボリックリンクになりますので、argv[0]に相談することは、合理的な答えではない - ではないと呼ばれる実行可能ファイルへの究極のパスargv[0]をもうそできダムアプリケーションがexec()を使用している場合。 argvを正しく初期化することを忘れてしまいました。

+1

argv [0]を読むことは解決策であり、このスレッドでは何も私には納得できません。 – bortzmeyer

+11

@bortzmeyer: 'execl("/home/hacker/.hidden/malicious "、"/bin/ls "、" -s "、(char *)0);' - argv [0] 'の値''/bin/ls ''ですが、これは実行可能ファイルの名前とは関係ありません。 –

答えて

44

関数_NSGetExecutablePathは、実行可能ファイルへのフルパス(GUIかどうか)を返します。パスにシンボリックリンク「..」などが含まれていてもかまいませんが、必要に応じてrealpath機能を使用してそれらをクリーンアップすることができます。詳細については、man3dyldを参照してください。

char path[1024]; 
uint32_t size = sizeof(path); 
if (_NSGetExecutablePath(path, &size) == 0) 
    printf("executable path is %s\n", path); 
else 
    printf("buffer too small; need size %u\n", size); 

この機能の秘密は、それがプロセスを作成するときにダーウィンカーネルがenvp配列の直後にプロセス・スタック上の実行可能ファイルのパスを置くことです。ダイナミックリンクエディタdyldは初期化時にこれを取得し、ポインタを保持します。この関数はそのポインタを使用します。

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <libproc.h> 

int main (int argc, char* argv[]) 
{ 
    int ret; 
    pid_t pid; 
    char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; 

    pid = getpid(); 
    ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf)); 
    if (ret <= 0) { 
     fprintf(stderr, "PID %d: proc_pidpath();\n", pid); 
     fprintf(stderr, " %s\n", strerror(errno)); 
    } else { 
     printf("proc %d: %s\n", pid, pathbuf); 
    } 

    return 0; 
} 
+0

最後の行が少し壊れていませんか? – ioquatix

+0

成功した場合は0を返し、バッファが十分に大きくなければ-1を返します。この場合、「size」が必要なサイズで埋められます。その場合、 'malloc()'でバッファを割り当てることができます。 – mark4o

+0

これは、4つの引数を持つmainを宣言することと同じです.4番目の引数には、安全なargv [0]が含まれます。 –

2

私は考えている保証はありません。 argv [0]がシンボリックリンクの場合、readlink()を使用できます。 コマンドが$ PATHを介して実行されている場合は、1が の一部試みることができる:答えはあなたができることであるように検索を(のgetenv( "PATH"))、のgetenv( "_")、dladdr()

+0

これは多くのケースをカバーしますが、あなたがアプリケーションによって起動された場合でも、argv [0]を適切に初期化するのを怠った場合には失敗します。 –

+0

このようなアプリケーションの例を挙げることはできますか? argvを初期化するアプリケーションではなく、libcであり、アプリケーションはargv [0]をスクランブルするために特別な処理を行う必要があります。 – bortzmeyer

+2

アプリケーションの例を私の頭の上から外しても間違っているわけではありませんが、argv [0]を壊すために必要なことは、execのいずれかを使ってアプリケーションを呼び出すときに正しく設定することを忘れてしまいます*コール。 libcは、system()を介してアプリケーションを呼び出す場合にのみ関与します。 –

3

は」ルックスtはそれを行う:私は lsofをの機能のようなものを達成し、統計情報とプロセスの実行に関する情報 の 全体の束を収集しようとしている

。 lsof の速度がそれほど遅くない場合は、 を貼っても幸いです。あなたはlsofを再実装した場合

、あなたはそれが仕事の 多くのことをやっているので、それは遅いです があります。

私は がユーザーモードでlsofを、それが 外部ページャに裏打ちされたものを探しているタスクのアドレス空間 を通じて スキャンを持っていることをより多くのだからそれは本当にないのですね。私が カーネルに入っているときに、より速く の方法がありますか?

番号lsofは愚かではありません。それは何をしなければならないか しています。 の機能のサブセットが必要な場合は ソース(利用可能)から開始し、 の要件を満たすように調整することができます。

好奇心の怪しい、p_textvpは で使用されていますか?それは 親のp_textvpkern_forkで(と はその後解放取得??)に設定されていますが、それはkern_execさん ルーチンのいずれにも触れ得ていない ているように見えます。

p_textvpは使用しません。ダーウィンでは、 procはアドレスのルートではありません スペース;タスクはです。タスク のアドレス空間の "vnode"という概念は ではありません。 には最初に がマッピングされています。

execがp_textvpを生成する場合、 は、 のすべてのプロセスがvnodeによってバックアップされていることを前提にしています。 はその後、プログラマはそれ が vノードへのパスを取得することが可能であったと仮定しますと、そこからは、vノードへ 電流経路は、それが起動されたパス があることを前提に短い のジャンプで、その 文字列のテキスト処理は アプリケーションバンドル名... につながる可能性がありますが、いずれも実質的なペナルティなしで 保証はできません。

Mike Smith, Darwin Drivers mailing list

+0

私は実際には、「あなたはできません」と言う答えを受け入れることを本当に嫌いですが、その引用は確かに私の質問の棺の釘をかなり痛ましく思います。 –

+1

ええ、私も答えを与えることは嫌いでした。私はこれを発見する前に、p_textvpから情報を取得する方法を見つけ出すことができるかどうかを調べるために、野生のガチョウの追跡にしばらく時間を費やしました。 –

0

なぜ単にrealpath(argv[0], actualpath);?実際のrealpathにはいくつかの制限がありますが(マニュアルページに記載されていますが)、シンボリックリンクをうまく処理します。 FreeBSDとLinuxの

 
    % ls -l foobar 
    lrwxr-xr-x 1 bortzmeyer bortzmeyer 22 Apr 29 07:39 foobar -> /tmp/get-real-name-exe 

    % ./foobar 
    My real path: /tmp/get-real-name-exe 
プログラムがPATH経由で起動された場合
#include <limits.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <libgen.h> 
#include <string.h> 
#include <sys/stat.h> 

int 
main(argc, argv) 
    int    argc; 
    char   **argv; 
{ 
    char   actualpath[PATH_MAX + 1]; 

    if (argc > 1) { 
     fprintf(stderr, "Usage: %s\n", argv[0]); 
     exit(1); 
    } 
    realpath(argv[0], actualpath); 
    fprintf(stdout, "My real path: %s\n", actualpath); 
    exit(0); 
} 

でテストされ、pixelbeatのソリューションを参照してください。

+0

ダムプログラムがexec *呼び出しであなたを呼び出し、argv [0]が実行可能な名前(つまり絶対パスではない)またはフラットアウトの間違い(absent、null文字列)になるようにargv構造体を不適切に初期化すると失敗します、またはあなたが何を持っているか)。 –

+1

argv [0]が完全なパスでない場合、問題はありません。realpath()はそれを処理します。それが空でもNULLでもあれば、それは私のプログラムではなく、呼び出し元の誤りです:-) – bortzmeyer

+0

@BenjaminPollack:実行ファイルが '$ PATH'にある場合、前者については何も遠隔からは間違いありません! – SamB

0
+0

アプリケーションがFinder経由で起動され、CarbonとリンクするGUIアプリケーションであれば動作します。しかしこの場合、[[NSBundle mainBundle] bundlePath] 'も動作し、' FSRef'の作成とプロセスのシリアル番号の特定は避けてください。 –

+0

@BenjaminPollack '[[NSBundle mainBundle] bundlePath]'は、 'Foundation'にリンクしている限り、単一のバイナリだけである非UIアプリケーションでも動作します。あなたのアプリケーションが単に 'CoreFoundation'にリンクすれば' CFBundle'を使うことができます。すべてのバンドルメソッドはバンドルのないプレーンバイナリでも動作しますが、有用な情報は必ずしも返ってくるとは限りませんが、バイナリの実行可能パスを取得するために機能します。 – Mecki

24

- ラインプログラム。

+1

私はあなたのソリューションが大好きですAlenは、魅力のように動作します – Dave

+2

ありがとう!ただし、OS X 10.8.5を使用していても、これは私にとってはうまくいきませんでした。#include Charlesism

+0

私が今までに得られる最良の解決策。非常にきれいな仕事! – Pal

1

これは遅れているが、非バンドルされ、コマンドの[[NSBundle mainBundle] executablePath]作品だけで罰金:私は実際にどのPIDで動作し、また、直接、絶対パスを返すはるかにエレガントな解決策があると信じて

関連する問題