क्या किसी प्रक्रिया के लिए एक विशिष्ट पथ को नकली करना संभव है?


9

मैं कई उपयोगकर्ताओं के साथ लिनक्स सर्वर पर एडीबी चलाने की कोशिश कर रहा हूं जहां मैं रूट नहीं हूं (अपने एंड्रॉइड एमुलेटर के साथ खेलने के लिए)। एडीबी डेमन ने अपने लॉग को फाइल में लिखा है /tmp/adb.logजो दुर्भाग्य से एडीबी में हार्ड-कोडेड लगता है और यह स्थिति बदलने वाली नहीं है

तो, स्पष्ट त्रुटि देते हुए, adb चलाना विफल हो रहा है cannot open '/tmp/adb.log': Permission denied:। यह फ़ाइल किसी अन्य उपयोगकर्ता द्वारा बनाई गई है और /tmpइसमें थोड़ा सा चिपचिपा है। अगर मैं adb nodaemon serverइसे stdout को लिखने के साथ adb करना शुरू करता हूं , तो कोई त्रुटि नहीं होती है (मैं टकराव से बचने के लिए इसके पोर्ट को एक अद्वितीय मान पर सेट करता हूं)।

मेरा प्रश्न है: क्या ADB को किसी अन्य फ़ाइल से लिखने के लिए कोई रास्ता है /tmp/adb.log? अधिक आम तौर पर, क्या एक प्रक्रिया-विशिष्ट सीमलिंक बनाने का एक तरीका है? मैं /tmp/adb.logएक फ़ाइल के लिए सभी फ़ाइल एक्सेस को रीडायरेक्ट करना चाहता हूं ~/tmp/adb.log

फिर से, मैं सर्वर पर रूट नहीं हूं, इसलिए chroot, mount -o rbindऔर chmodमान्य विकल्प नहीं हैं। यदि संभव हो तो, मैं एडीबी स्रोतों को संशोधित नहीं करना चाहूंगा, लेकिन निश्चित रूप से अगर कोई अन्य समाधान नहीं है, तो मैं ऐसा करूंगा।

पुनश्च विशिष्ट एडीबी मामले के लिए मैं इसके adb nodaemon serverसाथ चलने nohupऔर आउटपुट पुनर्निर्देशन का सहारा ले सकता हूं , लेकिन सामान्य प्रश्न अभी भी प्रासंगिक है।


2
हाँ। आप अपनी प्रक्रिया को किसी निजी माउंट नेमस्पेस में रख सकते हैं, और किसी अन्य फ़ाइल को /tmp/adb.logमाउंट कर सकते हैं, या /tmpपूरी तरह से अपना निजी माउंट कर सकते हैं। कर man unshareऔर man namespacesऔर man nsenter
13

1
@ माइकर्स ग्रेट, यह वही लगता है जो मुझे चाहिए, धन्यवाद! यदि आप अपनी टिप्पणी को एक उत्तर के रूप में सुधारते हैं, तो मैं इसे स्वीकार करने में सक्षम हो जाऊंगा।
gluk47

या फिर LD_PRELOADट्रिक्स हैं, हालांकि यह अधिक जटिल होगा।
त्रिगुट

@ हां, मैं हालांकि LD_PRELOAD के बारे में हूं, लेकिन स्पष्ट रूप से, यह हार्डकोड /home/$USER/tmp/adb.logऔर पुनर्निर्माण के लिए आसान होगा :)
gluk47

जवाबों:


5

यहाँ का उपयोग करने का एक बहुत ही सरल उदाहरण है util-linux's unshareएक निजी नाम स्थान माउंट में एक प्रक्रिया रख दिया और यह एक ही फाइल सिस्टम अपनी मूल वर्तमान का एक अलग दृश्य देने के लिए:

{   cd /tmp                      #usually a safe place for this stuff
    echo hey   >file             #some
    echo there >file2            #evidence
    sudo unshare -m sh -c '      #unshare requires root by default
         mount -B file2 file     #bind mount there over hey
         cat file                #show it
         kill -TSTP "$$"         #suspend root shell and switch back to parent
         umount file             #unbind there
         cat file'               #show it
    cat file                     #root shell just suspended
    fg                           #bring it back
    cat file2                    #round it off
}

there                            #root shell
hey                              #root shell suspended
hey                              #root shell restored
there                            #rounded

आप unshareअप-टू-डेट लिनक्स सिस्टम पर उपयोगिता के साथ इसके फाइल सिस्टम की एक निजी दृष्टि दे सकते हैं , हालांकि माउंट नेमस्पेस सुविधा स्वयं पूरे 3.x कर्नेल श्रृंखला के लिए काफी परिपक्व है। आप nsenterएक ही पैकेज से उपयोगिता के साथ सभी प्रकार के पहले से मौजूद नामस्थान दर्ज कर सकते हैं, और आप अधिक जानकारी प्राप्त कर सकते हैं man


बस एक ही सवाल: क्या यह मुझे है या यह एक सही समाधान है, लेकिन केवल रूट उपयोगकर्ता के लिए?
gluk47

@ gluk47 - यह होना जरूरी नहीं है। आप unshareसभी प्रकार के नामस्थानों को शामिल कर सकते हैं - उपयोगकर्ता नामस्थान को शामिल करने के लिए। और इसलिए आपका उपयोगकर्ता एक नामस्थान चला सकता है, जिसमें उसकी जड़ तक पहुँच हो और कुछ भी ऐसा हो जो जड़ उपयोगकर्ता को खराब कर सकता है, मूल नामस्थान को प्रभावित नहीं करता है। दूसरे शब्दों में, उपयोगकर्ता नाम स्थान के भीतर एक माउंट नेमस्पेस एम्बेड किया जा सकता है। आपको वास्तव में उन manपृष्ठों को पढ़ने की आवश्यकता है । यह गहरा हो जाता है। यह ठीक है कि कैसे dockerऔर sytemd-nspawnकाम करता है।
15

मैंने उन मैन पेजों और उदाहरणों को इंटरनेट से पढ़ा है) ऐसा लगता है कि मुझे उन्हें और अधिक पढ़ने की आवश्यकता है, बस इस तकनीक को इंगित करने के लिए धन्यवाद, मुझे किसी भी तरह से इसके बारे में पता नहीं था।
gluk47

@ gluk47 - वफादारी के लिए जवाब स्वीकार नहीं करते। जबकि भावना की सराहना की जाती है, उस तरह का सामान इस जगह के उद्देश्य को हरा देता है। आपके द्वारा उपयोग किए जाने वाले उत्तर को स्वीकार करें । यदि यह एक नहीं है, तो कृपया इस उत्तर को स्वीकार करें। वैसे, सिर्फ इसलिए कि एक प्रक्रिया को रूट के रूप में लॉन्च किया गया है, इसका मतलब यह नहीं है कि इसे रूट प्रक्रिया बने रहना है। ऐसी runuserउपयोगिता है जिसका उपयोग किया जा सकता है unshare, और यदि आप वैसे भी संकलित प्रोग्राम लिखने के लिए खुले हैं, तो ऐसा कोई कारण नहीं है कि आप unshare()syscall का उपयोग समान करने के लिए नहीं कर सकते हैं , या यहां तक ​​कि केवल system()suid बाइनरी के साथ भी ।
मिकसेर्व

मुझे यकीन है कि अगर यह उपयोगी नहीं था, तो जवाब को स्वीकार नहीं करूंगा। मुझे दोनों उत्तर प्रासंगिक और उपयोगी लगते हैं, इसलिए भावना इनमें से किसी एक उत्तर की जाँच करने का एकमात्र विशिष्ट कारण है :)
gluk47

11

LD_PRELOAD बहुत मुश्किल नहीं है, और आपको रूट होने की आवश्यकता नहीं है। अपनी खुद की सी दिनचर्या को इंटर करें जो open()सी लाइब्रेरी में वास्तविक के बजाय कहा जाता है। यदि फ़ाइल खोलने का आपका तरीका "/tmp/adb.log" है, तो आपकी नियमित जाँच होती है और वास्तविक फ़ाइल को किसी भिन्न फ़ाइल नाम से कॉल करता है। यहाँ आपका shim_open.c है:

/*
 * capture calls to a routine and replace with your code
 * gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c
 * LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log
 */
#define _FCNTL_H 1 /* hack for open() prototype */
#define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#define OLDNAME "/tmp/adb.log"
#define NEWNAME "/tmp/myadb.log"

int open(const char *pathname, int flags, mode_t mode){
    static int (*real_open)(const char *pathname, int flags, mode_t mode) = NULL;

    if (!real_open) {
        real_open = dlsym(RTLD_NEXT, "open");
        char *error = dlerror();
        if (error != NULL) {
            fprintf(stderr, "%s\n", error);
            exit(1);
        }
    }
    if (strcmp(pathname,OLDNAME)==0) pathname = NEWNAME;
    fprintf(stderr, "opening: %s\n", pathname);
    return real_open(pathname, flags, mode);
}

इसके साथ संकलित करें gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.cऔर इसमें कुछ डालकर /tmp/myadb.logऔर चलाकर परीक्षण करें LD_PRELOAD=/.../shim_open.so cat /tmp/adb.log। फिर adb पर LD_PRELOAD का प्रयास करें।


ठीक है, वास्तव में आपका समाधान केवल एक ही है कि मैं काम को एक गैर-रूट उपयोगकर्ता बनाने में कामयाब रहा। मैंने अनशेयर ( Operation not permitted) का सामना नहीं किया । मुझे उम्मीद है कि openसंभाल करने के लिए पर्याप्त है, लेकिन आखिरकार, unlinkइस हैंडलर को जोड़ना मुश्किल नहीं है।
gluk47

ओह। क्या शर्म की बात है कि मैं दो उत्तरों की जाँच नहीं कर सकता। मैंने एक उत्तर के रूप में उसके समाधान की जांच करने के लिए mikeserv का वादा किया, और यह वास्तव में एक व्यवहार्य है।
gluk47

2
कोई बात नहीं। मुझे इस बारे unshareमें पता चला , इसलिए हम सभी लाभान्वित हुए!
meuh

कुछ समय बाद, LD_PRELOAD नमूने के लिए फिर से धन्यवाद। चूंकि मैंने आपका कोड आज़माया है, इसलिए मैं विभिन्न परिस्थितियों में LD_PRELOAD का उपयोग कर रहा हूं जहां मैंने इसके बारे में सोचा भी नहीं था। मेरा जीवन बेहतर हो गया है :)
gluk47

2
@ gluk47 यह गनु / लिनक्स के बारे में इतना अद्भुत है: आपको कभी भी खोज बंद करने की आवश्यकता नहीं है! खोज और साझा करने के लिए बहुत अच्छी चीजें हैं।
meuh
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.