क्या सी के भीतर से शेल कमांड को कॉल करना एक अच्छा विचार है?


50

एक यूनिक्स शेल कमांड ( udevadm info -q path -n /dev/ttyUSB2) है जिसे मैं सी प्रोग्राम से कॉल करना चाहता हूं। लगभग एक सप्ताह के संघर्ष के साथ, मैं इसे स्वयं लागू कर सकता था, लेकिन मैं ऐसा नहीं करना चाहता।

क्या मेरे लिए सिर्फ कॉल करने के लिए व्यापक रूप से अच्छा अभ्यास स्वीकार किया गया है popen("my_command", "r");, या यह अस्वीकार्य सुरक्षा समस्याओं और आगे की संगतता मुद्दों को पेश करेगा? मुझे ऐसा कुछ करना गलत लगता है, लेकिन मैं इस बात पर उंगली नहीं उठा सकता कि यह बुरा क्यों होगा।


8
एक बात पर विचार करने के लिए इंजेक्शन हमलों है।
मेटाफाइट

8
मैं मुख्य रूप से उस मामले के बारे में सोच रहा था जहाँ आपने शेल कमांड आर्ग्युमेंट्स के रूप में उपयोगकर्ता इनपुट की आपूर्ति की थी।
मेटाफाइट

8
क्या कोई दुर्भावनापूर्ण udevadmनिष्पादन को आपके कार्यक्रम के समान निर्देशिका में रख सकता है और इसे विशेषाधिकार बढ़ाने के लिए उपयोग कर सकता है? मुझे यूनिक्स सुरक्षा के बारे में ज्यादा जानकारी नहीं है, लेकिन क्या आपका कार्यक्रम चलाया जाएगा setuid root?
एंड्रयू पिलिसर

7
@AndrewPiliser यदि वर्तमान निर्देशिका चालू नहीं है PATH, तो नहीं - इसके साथ चलना होगा ./udevadm। IIRC Windows समान-निर्देशिका निष्पादन योग्य चलाएगी, हालाँकि।
इजाकाता

4
जब भी संभव हो शेल के माध्यम से जाने के बिना वांछित कार्यक्रम को सीधे लागू करें।
कोडइन्चोस

जवाबों:


58

यह विशेष रूप से बुरा नहीं है, लेकिन कुछ चेतावनी हैं।

  1. आपका समाधान कितना पोर्टेबल होगा? क्या आपका चुना हुआ बाइनरी हर जगह एक ही काम करेगा, परिणाम एक ही प्रारूप में होगा आदि? यह LANGआदि की सेटिंग्स पर अलग उत्पादन होगा ?
  2. यह आपकी प्रक्रिया पर कितना अतिरिक्त भार डालता है? द्विआधारी परिणाम को बहुत अधिक लोड करने के लिए और लाइब्रेरी कॉल (आमतौर पर बोलने) को निष्पादित करने की तुलना में अधिक संसाधनों की आवश्यकता होती है। क्या यह आपके परिदृश्य में स्वीकार्य है?
  3. क्या सुरक्षा के मुद्दे हैं? क्या कोई आपके चुने हुए बाइनरी को दूसरे के साथ स्थानापन्न कर सकता है, और उसके बाद नापाक काम कर सकता है? क्या आप अपने बाइनरी के लिए उपयोगकर्ता-आपूर्ति किए गए आर्गों का उपयोग करते हैं, और क्या वे ;rm -rf /(उदाहरण के लिए) प्रदान कर सकते हैं (ध्यान दें कि कुछ एपीआई आपको कमांड लाइन पर उन्हें प्रदान करने की तुलना में अधिक सुरक्षित रूप से आर्ग्स निर्दिष्ट करने की अनुमति देंगे)

जब मैं एक ज्ञात वातावरण में हूं, तो मैं आम तौर पर खुश रहने वाले बायनेरी को खुश कर सकता हूं, जब मैं भविष्यवाणी कर सकता हूं, जब बाइनरी आउटपुट पार्स करना आसान है (यदि आवश्यक हो - आपको बस एक एक्जिट कोड की आवश्यकता हो सकती है) और मुझे इसे करने की आवश्यकता नहीं है अक्सर।

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


13
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking). अगर यह विंडोज पर होता, तो मैं सहमत होता। हालांकि, ओपी यूनिक्स को निर्दिष्ट करता है जहां फोर्किंग की लागत काफी कम है कि यह कहना मुश्किल है कि गतिशील रूप से लाइब्रेरी लोड करना फोर्किंग की तुलना में खराब है या नहीं।
wallyk

1
मुझे आमतौर पर लाइब्रेरी के एक बार लोड होने की उम्मीद है , जबकि बाइनरी को कई बार निष्पादित किया जा सकता है। हमेशा की तरह, यह उपयोग परिदृश्यों पर निर्भर है, लेकिन विचार करने की आवश्यकता है (यदि बाद में खारिज कर दिया गया है)
ब्रायन एग्न्यू

3
विंडोज पर कोई "फोर्किंग" नहीं है, इसलिए उद्धरण को यूनिक्स-विशिष्ट होना चाहिए।
कोड़ी ग्रे

5
NT कर्नेल फोर्किंग का समर्थन करता है, हालांकि यह Win32 के माध्यम से उजागर नहीं होता है। वैसे भी, मेरा मानना है कि एक बाहरी कार्यक्रम को चलाने की लागत से अधिक आ जाएगा execसे की तुलना में fork। लोड हो रहा है वर्गों, साझा वस्तुओं में जोड़ने, प्रत्येक के लिए init कोड चल रहा है। और फिर इनपुट और आउटपुट दोनों के लिए सभी डेटा को दूसरी तरफ पार्स किए जाने के लिए टेक्स्ट में बदलना होगा।
स्पेक्टर्स

8
निष्पक्ष होने के लिए, udevadm info -q path -n /dev/ttyUSB2विंडोज पर वैसे भी काम नहीं किया जा रहा है , इसलिए पूरे फोर्किंग की चर्चा थोड़ी सैद्धांतिक है।
एमएसलटर्स

37

एक बार जब आप एक संभावित वेक्टर पेश करते हैं, तो यह इंजेक्शन की कमजोरियों से बचाव के लिए अत्यधिक देखभाल करता है। यह अब आपके दिमाग में सबसे आगे है, लेकिन बाद में आपको चयन करने की क्षमता की आवश्यकता हो सकती है ttyUSB0-3, फिर उस सूची का उपयोग अन्य स्थानों पर किया जाएगा ताकि यह एकल जिम्मेदारी सिद्धांत का पालन करने के लिए फैक्टर हो जाए, फिर एक ग्राहक को रखना होगा सूची में एक मनमाना डिवाइस, और उस परिवर्तन को बनाने वाले डेवलपर को यह पता नहीं होगा कि सूची अंततः असुरक्षित तरीके से उपयोग की जाती है।

दूसरे शब्दों में, कोड के रूप में अगर आप जानते हैं कि सबसे लापरवाह डेवलपर कोड के प्रतीत होता है असंबंधित हिस्से में एक असुरक्षित बदलाव कर रहा है।

दूसरा, सीएलआई उपकरणों के उत्पादन को आम तौर पर स्थिर इंटरफेस नहीं माना जाता है जब तक कि दस्तावेज विशेष रूप से उन्हें इस तरह से चिह्नित नहीं करता है। आपके द्वारा चलाए जाने वाले स्क्रिप्ट के लिए आप उन पर भरोसा कर सकते हैं, जो आप अपने आप को परेशान कर सकते हैं, लेकिन किसी ऐसी चीज़ के लिए नहीं जिसे आप ग्राहक के लिए तैनात करते हैं।

तीसरा, यदि आप अपने सॉफ़्टवेयर से मूल्य निकालने का एक आसान तरीका चाहते हैं, तो संभावना है कि कोई दूसरा व्यक्ति भी यही चाहता है। एक पुस्तकालय की तलाश करें जो पहले से ही आप क्या चाहते हैं। libudevमेरे सिस्टम पर पहले से ही स्थापित था:

#include <libudev.h>
#include <sys/stat.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
    struct stat statbuf;

    if (stat("dev/ttyUSB2", &statbuf) < 0)
        return -1;
    struct udev* udev = udev_new();
    struct udev_device *dev = udev_device_new_from_devnum(udev, 'c', statbuf.st_rdev);

    printf("%s\n", udev_device_get_devpath(dev));

    udev_device_unref(dev);
    udev_unref(udev);
    return 0;
}

उस पुस्तकालय में अन्य उपयोगी कार्यक्षमता है। मेरा अनुमान है कि यदि आपको इसकी आवश्यकता है, तो संबंधित कुछ कार्य भी उपयोगी हो सकते हैं।


2
धन्यवाद। मैंने कामवासना की तलाश में इतनी देर लगाई। मैं अभी नहीं मिल रहा है!
जॉनी_बॉय

2
मैं नहीं देखता कि "इंजेक्शन भेद्यता" यहां एक मुद्दा है जब तक कि ओपी के कार्यक्रम को लागू नहीं किया जाता है जब किसी अन्य उपयोगकर्ता का उसके पर्यावरण पर नियंत्रण होता है, जो मुख्य रूप से सुसाइड की स्थिति में होता है (संभवतः भी, लेकिन कुछ हद तक, जैसी चीजें cgi और ssh मजबूर-कमांड)। कोई कारण नहीं है कि सामान्य कार्यक्रमों को उस उपयोगकर्ता के खिलाफ सख्त करने की आवश्यकता होती है जिसे वे चला रहे हैं।
R ..

2
@R ..: "इंजेक्शन भेद्यता" जब आप सामान्यीकरण ttyUSB2और उपयोग करते हैं sprintf(buf, "udevadm info -q path -n /dev/%s", argv[1])। अब जब कि काफी सुरक्षित प्रतीत होता है, लेकिन क्या हुआ अगर argv[1]है "nul || echo OOPS"?
एमएसलटर्स

3
@ आर ..: आपको बस अधिक कल्पना की आवश्यकता है। पहले यह एक निश्चित स्ट्रिंग है, फिर यह उपयोगकर्ता द्वारा प्रदान किया गया एक तर्क है, फिर यह उपयोगकर्ता द्वारा चलाए जा रहे कुछ अन्य प्रोग्राम द्वारा प्रदान किया गया एक तर्क है, लेकिन उन्हें समझ में नहीं आता है, तो यह एक तर्क है जिसे उनके ब्राउज़र ने एक वेब पेज से हटा दिया है। यदि चीजें "डाउनहिल" होती रहती हैं, तो अंततः किसी को इनपुट को सैनिटाइज़ करने की जिम्मेदारी लेनी होगी, और कार्ल कह रहा है कि यह उस बिंदु की पहचान करने के लिए अत्यधिक ध्यान रखता है, जहां भी यह आ सकता है।
स्टीव जेसोप

6
यद्यपि यदि एहतियाती सिद्धांत वास्तव में था "आप सबसे लापरवाह डेवलपर को मान लें कि आप जानते हैं कि एक असुरक्षित बदलाव कर रहा है", और मुझे यह गारंटी देने के लिए अब कोड लिखना होगा कि शोषण का परिणाम नहीं हो सकता है, फिर व्यक्तिगत रूप से मैं बिस्तर से बाहर नहीं निकल सकता। सबसे लापरवाह डेवलपर्स मुझे पता है कि मैकियावेली को ऐसा लगता है जैसे वह भी कोशिश नहीं कर रहा है
स्टीव जेसोप

16

आपके विशिष्ट मामले में, जहाँ आप आमंत्रित करना चाहते हैं udevadm, मुझे संदेह है कि आप udevएक पुस्तकालय के रूप में खींच सकते हैं और एक विकल्प के रूप में उपयुक्त फ़ंक्शन कॉल कर सकते हैं?

जैसे, आप क्या udevadm ही कर रही है जब "जानकारी" मोड में आह्वान है पर एक नज़र ले सकता है: https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c और समतुल्य कॉल कर के रूप में उन udvadm बना रहा है।

यह ब्रायन एग्न्यू के उत्कृष्ट उत्तर में उलटे-नीचे किए गए बहुत सारे व्यापारों से बचना होगा - उदाहरण के लिए, एक निश्चित पथ पर मौजूद बाइनरी पर निर्भर नहीं, फोर्किंग के खर्च से बचना, आदि।


धन्यवाद, मुझे वह सटीक रेपो मिल गया, और मैं इसे थोड़े समय के लिए देख रहा था।
johnny_boy

1
... और कामदेव विशेष रूप से उपयोग करने के लिए कठिन नहीं है। इसकी त्रुटि से निपटने में थोड़ी कमी है, लेकिन enumerating, क्वेरी और मॉनिटरिंग API बहुत सरल हैं। यदि आप इसे आजमाते हैं तो संघर्ष आपका डर 2 घंटे से कम हो सकता है।
स्पेक्टर्स

7

आपके प्रश्न से लगता है कि वन उत्तर के लिए कॉल किया गया था, और यहाँ उत्तर पेड़ के उत्तर की तरह प्रतीत होते हैं, इसलिए मैंने सोचा कि मैं आपको वन उत्तर दूंगा।

यह बहुत कम ही होता है कि C प्रोग्राम कैसे लिखे जाते हैं। यह हमेशा होता है कि शेल स्क्रिप्ट कैसे लिखी जाती है, और कभी-कभी पायथन, पर्ल या रूबी प्रोग्राम कैसे लिखे जाते हैं।

लोग आमतौर पर सिस्टम पुस्तकालयों के आसान उपयोग के लिए सी में लिखते हैं और ओएस सिस्टम कॉल के साथ-साथ गति के लिए निम्न-स्तरीय पहुंच का उपयोग करते हैं। और सी एक कठिन भाषा है, जिसमें लिखने के लिए लोगों को उन चीजों की आवश्यकता नहीं है, तो वे सी का उपयोग नहीं करते हैं। सी प्रोग्राम आमतौर पर केवल साझा पुस्तकालयों और कॉन्फ़िगरेशन फ़ाइलों पर निर्भरता की उम्मीद करते हैं।

एक उप-प्रक्रिया के लिए बाहर जाना विशेष रूप से तेज़ नहीं है, और इसके लिए निम्न-स्तरीय प्रणाली सुविधाओं के लिए ठीक-ठीक और नियंत्रित पहुंच की आवश्यकता नहीं होती है, और यह एक बाहरी निष्पादन योग्य पर संभवतः आश्चर्यजनक निर्भरता का परिचय देता है, इसलिए यह देखना असामान्य है C कार्यक्रमों में।

कुछ अतिरिक्त चिंताएं हैं। सुरक्षा और पोर्टेबिलिटी चिंताओं का उल्लेख लोग पूरी तरह से मान्य हैं। वे शेल स्क्रिप्ट के लिए समान रूप से मान्य हैं, लेकिन लोग शेल स्क्रिप्ट में उन प्रकार के मुद्दों की उम्मीद कर रहे हैं। लेकिन सी कार्यक्रमों में आमतौर पर सुरक्षा चिंता के इस वर्ग की उम्मीद नहीं की जाती है, जो इसे और अधिक खतरनाक बनाती है।

लेकिन, मेरी राय में, सबसे बड़ी चिंताओं को popenअपने कार्यक्रम के बाकी हिस्सों के साथ बातचीत करना होगा। popenएक बाल प्रक्रिया का निर्माण करना है, इसके उत्पादन को पढ़ना है और इसकी निकास स्थिति को इकट्ठा करना है। इस बीच, वह प्रक्रिया 'stderr आपके प्रोग्राम के रूप में उसी stderr से जुड़ी होगी, जो भ्रामक आउटपुट का कारण हो सकती है, और इसका स्टड आपके प्रोग्राम के समान होगा, जो अन्य दिलचस्प मुद्दों का कारण हो सकता है। आप इसे </dev/null 2>/dev/nullस्ट्रिंग में शामिल करके हल कर सकते हैं popenक्योंकि यह शेल द्वारा व्याख्या की गई है।

और popenएक बच्चे की प्रक्रिया बनाता है। यदि आप सिग्नल से निपटने या खुद से प्रक्रिया करने के लिए कुछ भी करते हैं तो आपको अजीब SIGCHLDसंकेत मिल सकते हैं। आपकी कॉल waitअजीब तरह से बातचीत कर सकती है popenऔर संभवत: अजीब स्थिति पैदा कर सकती है।

सुरक्षा और पोर्टेबिलिटी की चिंताएं हैं। जैसा कि वे शेल स्क्रिप्ट या किसी भी चीज के लिए हैं जो सिस्टम पर अन्य निष्पादनयोग्य को शुरू करता है। और आपको सावधान रहना होगा कि आपके प्रोग्राम का उपयोग करने वाले लोग आपके द्वारा पास किए गए स्ट्रिंग में शेल मेटा-चार्जर्स प्राप्त करने में सक्षम नहीं हैं popenक्योंकि यह स्ट्रिंग सीधे shसाथ दिया गया है sh -c <string from popen as a single argument>

लेकिन मुझे नहीं लगता कि वे सी प्रोग्राम का उपयोग करते हुए देखना अजीब है popen। यह अजीब है क्योंकि सी आमतौर पर एक निम्न स्तर की भाषा है, और popenनिम्न स्तर नहीं है। और क्योंकि popenआपके प्रोग्राम पर स्थानों की डिज़ाइन की कमी का उपयोग किया जाता है, क्योंकि यह आपके प्रोग्राम के मानक इनपुट और आउटपुट के साथ अजीब तरीके से बातचीत करेगा और इसे आपकी खुद की प्रक्रिया प्रबंधन या सिग्नल हैंडलिंग करने के लिए एक दर्द बना देगा। और क्योंकि C कार्यक्रमों में आमतौर पर बाहरी निष्पादकों पर निर्भरता की उम्मीद नहीं की जाती है।


0

आपका कार्यक्रम हैकिंग आदि के अधीन हो सकता है। इस प्रकार की गतिविधि से खुद को बचाने का एक तरीका यह है कि आप अपने वर्तमान मशीन एनवायरनमेंट की एक प्रति बनाएँ और एक प्रोग्राम का उपयोग करें जिसे चेरोट नामक प्रणाली का उपयोग करके चलाया जाए।

अपने कार्यक्रम के दृष्टिकोण से, एक सामान्य वातावरण में इसके निष्पादन, सुरक्षा पहलू से, अगर कोई आपके कार्यक्रम को तोड़ता है, तो इसका उपयोग केवल उन तत्वों तक पहुंच है जो आपने प्रतिलिपि बनाते समय प्रदान किए थे।

इस तरह के एक सेटअप क्रूट जेल कहा जाता है अधिक जानकारी के लिए देखने के क्रूट जेल

इसका उपयोग आम तौर पर सार्वजनिक रूप से सुलभ सर्वर आदि स्थापित करने के लिए किया जाता है।


3
ओपी अन्य लोगों को चलाने के लिए एक कार्यक्रम लिख रहा है, न कि अपने स्वयं के सिस्टम पर गैर-विश्वसनीय बायनेरी या स्रोत के साथ खेल रहा है।
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.