UNIX(7)

الاسم
unix - مقابس للتواصل بين العمليات محلياً

موجز
#include <sys/socket.h>
#include <sys/un.h>

unix_socket = socket(AF_UNIX, type, 0);
error = socketpair(AF_UNIX, type, 0, int *sv);

الوصف
عائلة المقبس AF_UNIX (المعروفة أيضًا باسم AF_LOCAL) تُستخدم للتواصل بين العمليات على نفس الجهاز بكفاءة. تقليدياً، يمكن أن تكون مقابس نطاق UNIX إما غير مسماة أو مرتبطة بمسار اسم ملف (مُعلّم كنوع مقبس). يدعم Linux أيضًا مساحة اسم مجردة مستقلة عن نظام الملفات.

أنواع المقابس الصالحة في نطاق UNIX هي: SOCK_STREAM، لمقبس موجه للتيار؛ SOCK_DGRAM، لمقبس موجه للبيانات يحافظ على حدود الرسائل (كما في معظم تطبيقات UNIX، مقابس بيانات نطاق UNIX موثوقة دائماً ولا تعيد ترتيب البيانات)؛ و (منذ Linux 2.6.4) SOCK_SEQPACKET، لمقبس حزم متسلسل موجه للاتصال، يحافظ على حدود الرسائل، ويُسلم الرسائل بالترتيب الذي أُرسلت به.

تدعم مقابس نطاق UNIX تمرير واصفات الملفات أو بيانات اعتماد العملية إلى عمليات أخرى باستخدام البيانات المساعدة.

  تنسيق العنوان
يُمثل عنوان مقبس نطاق UNIX في الهيكل التالي:

    struct sockaddr_un {
        sa_family_t sun_family;               /* AF_UNIX */
        char        sun_path[108];            /* Pathname */
    };

حقل sun_family يحتوي دائماً على AF_UNIX. في Linux، حجم sun_path هو 108 بايت؛ انظر أيضًا BUGS، أدناه.

تأخذ استدعاءات نظام متنوعة (مثل bind(2)، connect(2)، و sendto(2)) وسيط sockaddr_un كمدخل. تُرجع بعض استدعاءات النظام الأخرى (مثل getsockname(2)، getpeername(2)، recvfrom(2)، و accept(2)) وسيطاً من هذا النوع.

يُميز ثلاثة أنواع من العناوين في هيكل sockaddr_un:

pathname
    يمكن ربط مقبس نطاق UNIX بمسار اسم ملف منتهي بقيمة فارغة باستخدام bind(2). عندما يُعاد عنوان مقبس مسار اسم (بواسطة أحد استدعاءات النظام المذكورة أعلاه)، يكون طوله

        offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1

    و sun_path يحتوي على مسار الاسم المنتهي بقيمة فارغة. (في Linux، تعبير offsetof() أعلاه يساوي نفس قيمة sizeof(sa_family_t)، لكن بعض التطبيقات الأخرى تتضمن حقولاً أخرى قبل sun_path، لذا فإن تعبير offsetof() يصف حجم هيكل العنوان بشكل أكثر قابلية للنقل.)

    لمزيد من التفاصيل حول مقابس مسار الاسم، انظر أدناه.

unnamed
    مقبس تيار لم يُربط بمسار اسم باستخدام bind(2) ليس له اسم. بالمثل، المقبسان اللذان يُنشآن بواسطة socketpair(2) غير مسميين. عندما يُعاد عنوان مقبس غير مسمى، يكون طوله sizeof(sa_family_t)، ولا يجب فحص sun_path.

abstract
    يُميز عنوان مقبس مجرد (عن مقبس مسار اسم) بحقيقة أن sun_path[0] هو بايت فارغ ('\0'). عنوان المقبس في هذه المساحة الاسمية يُعطى بالبايتات الإضافية في sun_path التي تُغطى بالطول المحدد لهيكل العنوان. (البايتات الفارغة في الاسم ليس لها دلالة خاصة.) الاسم ليس له اتصال بمسارات أسماء الملفات. عندما يُعاد عنوان مقبس مجرد، يكون addrlen المُعاد أكبر من sizeof(sa_family_t) (أي أكبر من 2)، واسم المقبس موجود في أول (addrlen - sizeof(sa_family_t)) بايت من sun_path.

  مقابس مسار الاسم
عند ربط مقبس بمسار اسم، يجب مراعاة بعض القواعد لتحقيق أقصى قابلية للنقل وسهولة البرمجة:

•   مسار الاسم في sun_path يجب أن يكون منتهياً بقيمة فارغة.

•   طول مسار الاسم، بما في ذلك البايت الفارغ الختامي، يجب ألا يتجاوز حجم sun_path.

•   وسيط addrlen الذي يصف هيكل sockaddr_un المحيط يجب أن يكون له قيمة على الأقل:

        offsetof(struct sockaddr_un, sun_path)+strlen(addr.sun_path)+1

    أو، بشكل أبسط، يمكن تحديد addrlen كـ sizeof(struct sockaddr_un).

هناك بعض الاختلاف في كيفية معالجة التطبيقات لعناوين مقابس نطاق UNIX التي لا تتبع القواعد أعلاه. على سبيل المثال، بعض التطبيقات (وليس كلها) تُلحق فاصلاً فارغاً إذا لم يكن موجوداً في sun_path المُقدم.

عند برمجة تطبيقات قابلة للنقل، ضع في اعتبارك أن بعض التطبيقات لديها sun_path بطول 92 بايت فقط.

تُرجع استدعاءات نظام متنوعة (accept(2)، recvfrom(2)، getsockname(2)، getpeername(2)) هياكل عنوان مقبس. عند تطبيقها على مقابس نطاق UNIX، يجب تهيئة وسيط addrlen ذو القيمة-النتيجة المُقدم للاستدعاء كما هو مذكور أعلاه. عند العودة، يُضبط الوسيط للإشارة إلى الحجم الفعلي لهيكل العنوان. يجب على المستدعي التحقق من القيمة المُعادة في هذا الوسيط: إذا تجاوزت قيمة الإخراج قيمة الإدخال، فلا يوجد ضمان بوجود فاصل فارغ في sun_path. (انظر BUGS.)

  ملكية وصلات أسماء المسارات وأذوناتها
في تطبيق لينكس، تلتزم وصلات أسماء المسارات بأذونات الدليل الذي توجد فيه. يفشل إنشاء وصلة جديدة إذا لم تمتلك العملية إذن الكتابة والبحث (التنفيذ) على الدليل الذي تُنشأ فيه الوصلة.

في لينكس، يتطلب الاتصال بكائن وصلة دفق إذن كتابة على تلك الوصلة؛ إرسال مخطط بيانات إلى وصلة مخطط بيانات يتطلب بالمثل إذن كتابة على تلك الوصلة. لا يُدلي POSIX بأي بيان حول تأثير الأذونات على ملف وصلة، وفي بعض الأنظمة (مثل أنظمة BSD القديمة)، تُتجاهل أذونات الوصلة. لا ينبغي للبرامج المحمولة الاعتماد على هذه الميزة للأمان.

عند إنشاء وصلة جديدة، يُعيّن المالك والمجموعة لملف الوصلة وفقًا للقواعد المعتادة. يمتلك ملف الوصلة جميع الأذونات المُمكّنة، باستثناء تلك التي تُعطّل بواسطة umask(2) للعملية.

يمكن تغيير المالك والمجموعة وأذونات وصلة اسم المسار (باستخدام chown(2) وchmod(2)).

  الوصلات المجردة
لا معنى لأذونات الوصلة للوصلات المجردة: ليس لـ umask(2) للعملية أي تأثير عند ربط وصلة مجردة، وتغيير ملكية وأذونات الكائن (عبر fchown(2) وfchmod(2)) ليس له أي تأثير على إمكانية الوصول إلى الوصلة.

تختفي الوصلات المجردة آليًا عند إغلاق جميع المراجع المفتوحة للوصلة.

مساحة أسماء الوصلة المجردة هي امتداد لينكس غير محمول.

  خيارات المقبس
لأسباب تاريخية، تُحدد خيارات الوصلة هذه بنوع SOL_SOCKET على الرغم من أنها خاصة بـ AF_UNIX. يمكن تعيينها باستخدام setsockopt(2) وقراءتها باستخدام getsockopt(2) بتحديد SOL_SOCKET كعائلة الوصلة.

SO_PASSCRED
    يؤدي تمكين خيار الوصلة هذا إلى استلام بيانات اعتماد العملية المرسلة في رسالة SCM_CREDENTIALS التابعة في كل رسالة تُستلم لاحقًا. بيانات الاعتماد المُعادة هي تلك التي حددها المرسل باستخدام SCM_CREDENTIALS، أو مبدئي يتضمن معرف العملية ومعرف المستخدم الحقيقي ومعرف المجموعة الحقيقي للمرسل، إذا لم يحدد المرسل بيانات SCM_CREDENTIALS التابعة.

    عند تعيين هذا الخيار ولم تكن الوصلة متصلة بعد، يُنشأ اسم فريد في مساحة الأسماء المجردة آليًا.

    القيمة المعطاة كمعامل لـ setsockopt(2) والراجعة كنتيجة لـ getsockopt(2) هي علامة منطقية صحيحة.

SO_PASSSEC
    يُمكّن استلام تسمية أمان SELinux لوصلة النظير في رسالة تابعة من النوع SCM_SECURITY (انظر أدناه).

    القيمة المعطاة كمعامل لـ setsockopt(2) والراجعة كنتيجة لـ getsockopt(2) هي علامة منطقية صحيحة.

    خيار SO_PASSSEC مدعوم لوصلات مخطط بيانات نطاق يونكس منذ لينكس 2.6.18؛ أُضيف الدعم لوصلات دفق نطاق يونكس في لينكس 4.2.

SO_PEEK_OFF
    انظر socket(7).

SO_PEERCRED
    خيار الوصلة القابل للقراءة فقط هذا يُرجع بيانات اعتماد عملية النظير المتصلة بهذه الوصلة. بيانات الاعتماد المُعادة هي تلك التي كانت سارية المفعول في وقت استدعاء connect(2) أو listen(2) أو socketpair(2).

    الوسيط لـ getsockopt(2) هو مؤشر لبنية ucred؛ عرّف ماكرو اختبار الميزة _GNU_SOURCE للحصول على تعريف تلك البنية من <sys/socket.h>.

    استخدام هذا الخيار ممكن فقط لوصلات دفق AF_UNIX المتصلة ولأزواج وصلات دفق ومخطط بيانات AF_UNIX المُنشأة باستخدام socketpair(2).

  ميزة الربط الآلي
إذا حدد استدعاء bind(2) addrlen كـ sizeof(sa_family_t)، أو تم تحديد خيار الوصلة SO_PASSCRED لوصلة لم تُربط صراحة بعنوان، فتُربط الوصلة آليًا بعنوان مجرد. يتكون العنوان من بايت فارغ متبوع بـ 5 بايتات في مجموعة الأحرف [0-9a-f]. وبالتالي، هناك حد يبلغ 2^20 عنوان ربط آلي. (من لينكس 2.1.15، عند إضافة ميزة الربط الآلي، استُخدمت 8 بايتات، وبالتالي كان الحد 2^32 عنوان ربط آلي. جاء التغيير إلى 5 بايتات في لينكس 2.3.15.)

  واجهة برمجة تطبيقات المقابس (Sockets API)
تصف الفقرات التالية تفاصيل خاصة بالنطاق وميزات غير مدعومة لواجهة برمجة تطبيقات الوصلات لوصلات نطاق يونكس في لينكس.

لا تدعم وصلات نطاق يونكس إرسال البيانات خارج النطاق (علامة MSG_OOB لـ send(2) وrecv(2)).

علامة MSG_MORE لـ send(2) غير مدعومة بواسطة وصلات نطاق يونكس.

قبل لينكس 3.4، لم يكن استخدام MSG_TRUNC في وسيط flags لـ recv(2) مدعومًا بواسطة وصلات نطاق يونكس.

خيار الوصلة SO_SNDBUF له تأثير على وصلات نطاق يونكس، لكن خيار SO_RCVBUF ليس له تأثير. لوصلات مخطط البيانات، تفرض قيمة SO_SNDBUF حدًا أعلى على حجم مخططات البيانات الصادرة. يُحسب هذا الحد كقيمة الخيار المضاعفة (انظر socket(7)) ناقص 32 بايتًا مستخدمة للنفقات العامة.

  رسائل مساعدة
تُرسَل وتُستقبَل البيانات المساعدة باستخدام sendmsg(2) وrecvmsg(2). لأسباب تاريخية، تُحدَّد أنواع الرسائل المساعدة المدرجة أدناه بنوع SOL_SOCKET رغم أنها خاصة بـAF_UNIX. لإرسالها، اضبط حقل cmsg_level من البنية cmsghdr على SOL_SOCKET وحقل cmsg_type على النوع. لمزيد من المعلومات، انظر cmsg(3).

SCM_RIGHTS
    أرسل أو استقبل مجموعة من واصفات الملفات المفتوحة من عملية أخرى. يحتوي جزء البيانات على مصفوفة أعداد صحيحة لواصفات الملفات.

    يُشار إلى هذه العملية عادةً باسم "تمرير واصف ملف" إلى عملية أخرى. لكن، بشكل أكثر دقة، ما يُمرَّر هو مرجع إلى وصف ملف مفتوح (انظر open(2))، ومن المحتمل أن يُستخدم رقم واصف ملف مختلف في العملية المستقبلة. دلاليًا، هذه العملية مكافئة لنسخ (dup(2)) واصف ملف في جدول واصفات الملفات لعملية أخرى.

    إذا كانت المخبأة المستخدمة لاستقبال البيانات المساعدة المحتوية على واصفات الملفات صغيرة جدًا (أو غائبة)، فتُبتَر البيانات المساعدة (أو تُهمَل) وتُغلَق واصفات الملفات الزائدة آليًا في العملية المستقبلة.

    إذا تسبب عدد واصفات الملفات المستلمة في البيانات المساعدة في تجاوز العملية لحد مواردها RLIMIT_NOFILE (انظر getrlimit(2))، فتُغلَق واصفات الملفات الزائدة آليًا في العملية المستقبلة.

    يُعرِّف الثابت النواة SCM_MAX_FD حدًا لعدد واصفات الملفات في المصفوفة. تؤدي محاولة إرسال مصفوفة أكبر من هذا الحد إلى فشل sendmsg(2) مع الخطأ EINVAL. قيمة SCM_MAX_FD هي 253 (أو 255 قبل Linux 2.6.38).

SCM_CREDENTIALS
    أرسل أو استقبل بيانات اعتماد UNIX. يمكن استخدام هذا للاستيثاق. تُمرَّر بيانات الاعتماد كرسالة مساعدة من نوع struct ucred. تُعرَّف هذه البنية في <sys/socket.h> كما يلي:

        struct ucred {
            pid_t pid;    /* Process ID of the sending process */
            uid_t uid;    /* User ID of the sending process */
            gid_t gid;    /* Group ID of the sending process */
        };

    منذ glibc 2.8، يجب تعريف ماكرو اختبار الميزة _GNU_SOURCE (قبل تضمين أي ملفات رأس) للحصول على تعريف هذه البنية.

    تُفحَص بيانات الاعتماد التي يُحدِّدها المرسل بواسطة النواة. يُسمح لعملية متميزة بتحديد قيم لا تطابق قيمها الخاصة. يجب على المرسل تحديد معرف العملية الخاص به (إلا إذا كان لديه القدرة CAP_SYS_ADMIN، وفي هذه الحالة يمكن تحديد PID لأي عملية موجودة)، ومعرف المستخدم الحقيقي، أو معرف المستخدم الفعال، أو معرف المستخدم المحفوظ (إلا إذا كان لديه CAP_SETUID)، ومعرف المجموعة الحقيقي، أو معرف المجموعة الفعال، أو معرف المجموعة المحفوظ (إلا إذا كان لديه CAP_SETGID).

    لاستقبال رسالة struct ucred، يجب تمكين الخيار SO_PASSCRED على المقبس.

SCM_SECURITY
    استقبل سياق أمان SELinux (وسم الأمان) لمقبس النظير. البيانات المساعدة المستلمة هي سلسلة منتهية بقيمة خالية تحتوي على سياق الأمان. يجب على المستقبل تخصيص NAME_MAX بايت على الأقل في جزء البيانات من الرسالة المساعدة لهذه البيانات.

    لاستقبال سياق الأمان، يجب تمكين الخيار SO_PASSSEC على المقبس (انظر أعلاه).

عند إرسال بيانات مساعدة باستخدام sendmsg(2)، يمكن تضمين عنصر واحد فقط من كل نوع من الأنواع المذكورة أعلاه في الرسالة المرسلة.

يجب إرسال بايت واحد على الأقل من البيانات الحقيقية عند إرسال بيانات مساعدة. على Linux، هذا مطلوب لإرسال البيانات المساعدة بنجاح عبر مقبس دفق نطاق UNIX. عند إرسال بيانات مساعدة عبر مقبس مخطط بيانات نطاق UNIX، ليس من الضروري على Linux إرسال أي بيانات حقيقية مصاحبة. لكن، يجب على التطبيقات المحمولة تضمين بايت واحد على الأقل من البيانات الحقيقية عند إرسال بيانات مساعدة عبر مقبس مخطط بيانات.

عند الاستقبال من مقبس دفق، تشكل البيانات المساعدة نوعًا من الحاجز للبيانات المستلمة. على سبيل المثال، افترض أن المرسل يُرسل كما يلي:

    (1) sendmsg(2) لأربعة بايتات، بدون بيانات مساعدة.
    (2) sendmsg(2) لبايت واحد، مع بيانات مساعدة.
    (3) sendmsg(2) لأربعة بايتات، بدون بيانات مساعدة.

افترض أن المستقبل يُنفِّذ الآن استدعاءات recvmsg(2) كل منها بحجم مخبأة 20 بايت. سيستقبل الاستدعاء الأول خمسة بايتات من البيانات، إلى جانب البيانات المساعدة المرسلة بواسطة استدعاء sendmsg(2) الثاني. سيستقبل الاستدعاء التالي البايتات الأربعة المتبقية من البيانات.

إذا كانت المساحة المخصصة لاستقبال البيانات المساعدة الواردة صغيرة جدًا، فتُبتَر البيانات المساعدة إلى عدد الرؤوس التي تناسب المخبأة المقدمة (أو، في حالة قائمة واصفات ملفات SCM_RIGHTS، قد تُبتَر قائمة واصفات الملفات). إذا لم تُقدم مخبأة للبيانات المساعدة الواردة (أي، حقل msg_control من بنية msghdr المقدمة إلى recvmsg(2) هو NULL)، فتُهمَل البيانات المساعدة الواردة. في كلتا هاتين الحالتين، سيُضبط العلم MSG_CTRUNC في قيمة msg.msg_flags التي يُرجعها recvmsg(2).

  Ioctls
استدعاءات ioctl(2) التالية تعيد معلومات في value. الصيغة الصحيحة هي:

    int value;
    error = ioctl(unix_socket, ioctl_type, &value);

يمكن أن يكون ioctl_type:

SIOCINQ
    بالنسبة لمقابس SOCK_STREAM، يُرجع هذا الاستدعاء عدد البايتات غير المقروءة في مخبأة الاستقبال. يجب ألا يكون المقبس في حالة LISTEN، وإلا يُرجع خطأ (EINVAL). يُعرَّف SIOCINQ في <linux/sockios.h>. بدلاً من ذلك، يمكنك استخدام FIONREAD المترادف، المُعرَّف في <sys/ioctl.h>. بالنسبة لمقابس SOCK_DGRAM، القيمة المُرجَعة هي نفسها لمقابس مخطط بيانات نطاق الإنترنت؛ انظر udp(7).

الأخطاء

EADDRINUSE
    العنوان المحلي المحدد قيد الاستخدام بالفعل أو كائن مقبس نظام الملفات موجود بالفعل.

EBADF
    يمكن أن يحدث هذا الخطأ لـsendmsg(2) عند إرسال واصف ملف كبيانات مساعدة عبر مقبس نطاق UNIX (انظر وصف SCM_RIGHTS أعلاه)، ويشير إلى أن رقم واصف الملف الذي يُرسَل غير صالح (مثلًا، ليس واصف ملف مفتوح).

ECONNREFUSED
    العنوان البعيد المحدد بواسطة connect(2) لم يكن مقبس استماع. يمكن أن يحدث هذا الخطأ أيضًا إذا لم يكن اسم المسار الهدف مقبسًا.

ECONNRESET
    أُغلقت المقبس البعيد بشكل غير متوقع.

EFAULT
    عنوان ذاكرة المستخدم لم يكن صالحًا.

EINVAL
    مُمررت وسيطة غير صالحة. سبب شائع هو أن القيمة AF_UNIX لم تُحدد في حقل sun_type للعناوين المُمررة، أو أن المقبس كان في حالة غير صالحة للعملية المطبقة.

EISCONN
    استُدعيت connect(2) على مقبس موصول بالفعل أو حُدد عنوان هدف على مقبس موصول.

ENFILE
    وُصل إلى الحد الأقصى لإجمالي عدد الملفات المفتوحة على مستوى النظام.

ENOENT
    اسم المسار في العنوان البعيد المُحدد لـ connect(2) لم يكن موجودًا.

ENOMEM
    نفدت الذاكرة.

ENOTCONN
    عملية المقبس تحتاج عنوان هدف، لكن المقبس غير موصول.

EOPNOTSUPP
    استُدعيت عملية دفق على مقبس غير موجه للدفق أو حُاول استخدام خيار البيانات خارج النطاق.

EPERM
    مُمرر المرسل بيانات استيثاق غير صالحة في struct ucred.

EPIPE
    أُغلق المقبس البعيد على مقبس دفق. إذا كان مُفعلاً، يُرسل SIGPIPE أيضًا. يمكن تجنب ذلك بتمرير العلم MSG_NOSIGNAL إلى send(2) أو sendmsg(2).

EPROTONOSUPPORT
    البروتوكول المُمرر ليس AF_UNIX.

EPROTOTYPE
    المقبس البعيد لا يطابق نوع المقبس المحلي (SOCK_DGRAM مقابل SOCK_STREAM).

ESOCKTNOSUPPORT
    نوع مقبس غير معروف.

ESRCH
    أثناء إرسال رسالة مساعدة تحتوي على بيانات استيثاق (SCM_CREDENTIALS)، حدد المتصل PID لا يطابق أي عملية موجودة.

ETOOMANYREFS
    يمكن أن يحدث هذا الخطأ لـ sendmsg(2) عند إرسال واصف ملف كبيانات مساعدة عبر مقبس نطاق UNIX (انظر وصف SCM_RIGHTS، أعلاه). يحدث إذا تجاوز عدد واصفات الملفات "قيد الطيران" حد المورد RLIMIT_NOFILE ولم يكن لدى المتصل القدرة CAP_SYS_RESOURCE. واصف الملف قيد الطيران هو الذي أُرسل باستخدام sendmsg(2) لكن لم يُقبل بعد في عملية المستلم باستخدام recvmsg(2).

    شُخص هذا الخطأ منذ النواة الرئيسية Linux 4.5 (وفي بعض إصدارات النواة السابقة حيث رُجع الإصلاح). في إصدارات النواة السابقة، كان من الممكن وضع عدد غير محدود من واصفات الملفات قيد الطيران، بإرسال كل واصف ملف مع sendmsg(2) ثم إغلاق واصف الملف بحيث لا يُحتسب ضد حد المورد RLIMIT_NOFILE.

يمكن توليد أخطاء أخرى بواسطة طبقة المقابس العامة أو بواسطة نظام الملفات أثناء توليد كائن مقبس نظام الملفات. راجع صفحات الدليل المناسبة لمزيد من المعلومات.

الإصدارات
قُدما SCM_CREDENTIALS ومساحة الاسم المجردة مع Linux 2.2 ولا ينبغي استخدامهما في البرامج المحمولة. (بعض الأنظمة المشتقة من BSD تدعم أيضًا تمرير بيانات الاستيثاق، لكن تفاصيل التنفيذ تختلف.)

ملاحظات
الربط بمقبس باسم ملف ينشئ مقبسًا في نظام الملفات يجب حذفه بواسطة المتصل عندما لا يعود مطلوبًا (باستخدام unlink(2)). تنطبق دلالات الإغلاق اللاحق المعتادة لـ UNIX؛ يمكن فك ربط المقبس في أي وقت وسيُزال نهائيًا من نظام الملفات عند إغلاق آخر مرجع له.

لتمرير واصفات ملفات أو بيانات استيثاق عبر مقبس SOCK_STREAM، يجب إرسال أو استقبال بايت واحد على الأقل من بيانات غير مساعدة في نفس استدعاء sendmsg(2) أو recvmsg(2).

مقابس دفق نطاق UNIX لا تدعم مفهوم البيانات خارج النطاق.

العلل
عند ربط مقبس بعنوان، Linux هو أحد التطبيقات التي تُلحق مُنهيًا فارغًا إذا لم يُقدم أي في sun_path. في معظم الحالات هذا غير إشكالي: عند استرجاع عنوان المقبس، سيكون أطول ببايت واحد مما قُدم عند ربط المقبس. لكن، هناك حالة واحدة يمكن أن ينتج عنها سلوك مربك: إذا قُدمت 108 بايتات غير فارغة عند ربط مقبس، فإن إضافة المُنهي الفارغ تأخذ طول اسم المسار إلى ما بعد sizeof(sun_path). وبالتالي، عند استرجاع عنوان المقبس (مثلاً، عبر accept(2))، إذا حُددت وسيطة addrlen المدخلة لاستدعاء الاسترجاع كـ sizeof(struct sockaddr_un)، فإن بنية العنوان المُعادة لن تحتوي على مُنهٍ فارغ في sun_path.

بالإضافة إلى ذلك، بعض التطبيقات لا تتطلب مُنهيًا فارغًا عند ربط مقبس (تُستخدم وسيطة addrlen لتحديد طول sun_path) وعند استرجاع عنوان المقبس على هذه التطبيقات، لا يوجد مُنهٍ فارغ في sun_path.

يمكن للتطبيقات التي تسترجع عناوين المقابس أن تُبرمج (بشكل محمول) للتعامل مع احتمالية عدم وجود مُنهٍ فارغ في sun_path باحترام حقيقة أن عدد البايتات الصالحة في اسم المسار هو:

    strnlen(addr.sun_path, addrlen - offsetof(sockaddr_un, sun_path))

بدلاً من ذلك، يمكن للتطبيق استرجاع عنوان المقبس بتخصيص مخزن مؤقت بحجم sizeof(struct sockaddr_un)+1 يُصفّر قبل الاسترجاع. يمكن لاستدعاء الاسترجاع تحديد addrlen كـ sizeof(struct sockaddr_un)، والبايت الصفري الإضافي يضمن وجود مُنهٍ فارغ للسلسلة المُعادة في sun_path:

    void *addrp;

    addrlen = sizeof(struct sockaddr_un);
    addrp = malloc(addrlen + 1);
    if (addrp == NULL)
        /* Handle error */ ;
    memset(addrp, 0, addrlen + 1);

    if (getsockname(sfd, (struct sockaddr *) addrp, &addrlen)) == -1)
        /* handle error */ ;

    printf("sun_path = %s\n", ((struct sockaddr_un *) addrp)->sun_path);

يمكن تجنب هذا النوع من الفوضى إذا تم ضمان أن التطبيقات التي تنشئ مقابس اسم المسار تتبع القواعد الموضحة أعلاه تحت مقابس اسم المسار.

أمثلة
يظهر الكود التالي استخدام مقابس الحزم المتسلسلة للاتصال بين العمليات المحلية. يتكون من برنامجين. ينتظر برنامج الخادم اتصالاً من برنامج العميل. يرسل العميل كل وسيط من وسائط سطر الأوامر في رسائل منفصلة. يعامل الخادم الرسائل الواردة كأعداد صحيحة ويجمعها. يرسل العميل سلسلة الأمر "END". يرسل الخادم رسالة تحتوي على مجموع أعداد العميل الصحيحة. يطبع العميل المجموع ويخرج. ينتظر الخادم اتصال العميل التالي. لإيقاف الخادم، يُستدعى العميل مع وسيط سطر الأوامر "DOWN".

تم تسجيل المخرجات التالية أثناء تشغيل الخادم في الخلفية وتنفيذ العميل بشكل متكرر. ينتهي تنفيذ برنامج الخادم عند استلامه الأمر "DOWN".

  مثال على الخرج
    $ ./server &
    [1] 25887
    $ ./client 3 4;
    Result = 7
    $ ./client 11 -5;
    Result = 6
    $ ./client DOWN;
    Result = 0
    [1]+  Done                    ./server
    $

  مصدر البرنامج
    /*
     * File connection.h
     */
    #ifndef CONNECTION_H
    #define CONNECTION_H

    #define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket"
    #define BUFFER_SIZE 12

    #endif  // include guard

    /*
     * File server.c
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    #include <unistd.h>

    #include "connection.h"

    int
    main(void)
    {
        int                 down_flag = 0;
        int                 ret;
        int                 connection_socket;
        int                 data_socket;
        int                 result;
        ssize_t             r, w;
        struct sockaddr_un  name;
        char                buffer[BUFFER_SIZE];

        /* Create local socket.  */

        connection_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
        if (connection_socket == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }

        /*
         * For portability clear the whole structure, since some
         * implementations have additional (nonstandard) fields in
         * the structure.
         */

        memset(&name, 0, sizeof(name));

        /* Bind socket to socket name.  */

        name.sun_family = AF_UNIX;
        strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);

        ret = bind(connection_socket, (const struct sockaddr *) &name,
                   sizeof(name));
        if (ret == -1) {
            perror("bind");
            exit(EXIT_FAILURE);
        }

        /*
         * Prepare for accepting connections.  The backlog size is set
         * to 20.  So while one request is being processed other requests
         * can be waiting.
         */

        ret = listen(connection_socket, 20);
        if (ret == -1) {
            perror("listen");
            exit(EXIT_FAILURE);
        }

        /* This is the main loop for handling connections.  */

        for (;;) {

            /* Wait for incoming connection.  */

            data_socket = accept(connection_socket, NULL, NULL);
            if (data_socket == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }

            result = 0;
            for (;;) {

                /* Wait for next data packet.  */

                r = read(data_socket, buffer, sizeof(buffer));
                if (r == -1) {
                    perror("read");
                    exit(EXIT_FAILURE);
                }

                /* Ensure buffer is 0-terminated.  */

                buffer[sizeof(buffer) - 1] = 0;

                /* Handle commands.  */

                if (!strncmp(buffer, "DOWN", sizeof(buffer))) {
                    down_flag = 1;
                    continue;
                }

                if (!strncmp(buffer, "END", sizeof(buffer))) {
                    break;
                }

                if (down_flag) {
                    continue;
                }

                /* Add received summand.  */

                result += atoi(buffer);
            }

            /* Send result.  */

            sprintf(buffer, "%d", result);
            w = write(data_socket, buffer, sizeof(buffer));
            if (w == -1) {
                perror("write");
                exit(EXIT_FAILURE);
            }

            /* Close socket.  */

            close(data_socket);

            /* Quit on DOWN command.  */

            if (down_flag) {
                break;
            }
        }

        close(connection_socket);

        /* Unlink the socket.  */

        unlink(SOCKET_NAME);

        exit(EXIT_SUCCESS);
    }

    /*
     * File client.c
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/un.h>
    #include <unistd.h>

    #include "connection.h"

    int
    main(int argc, char *argv[])
    {
        int                 ret;
        int                 data_socket;
        ssize_t             r, w;
        struct sockaddr_un  addr;
        char                buffer[BUFFER_SIZE];

        /* Create local socket.  */

        data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
        if (data_socket == -1) {
            perror("socket");
            exit(EXIT_FAILURE);
        }

        /*
         * For portability clear the whole structure, since some
         * implementations have additional (nonstandard) fields in
         * the structure.
         */

        memset(&addr, 0, sizeof(addr));

        /* Connect socket to socket address.  */

        addr.sun_family = AF_UNIX;
        strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);

        ret = connect(data_socket, (const struct sockaddr *) &addr,
                       sizeof(addr));
        if (ret == -1) {
            fprintf(stderr, "The server is down.\n");
            exit(EXIT_FAILURE);
        }

        /* Send arguments.  */

        for (int i = 1; i < argc; ++i) {
            w = write(data_socket, argv[i], strlen(argv[i]) + 1);
            if (w == -1) {
                perror("write");
                break;
            }
        }

        /* Request result.  */

        strcpy(buffer, "END");
        w = write(data_socket, buffer, strlen(buffer) + 1);
        if (w == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }

        /* Receive result.  */

        r = read(data_socket, buffer, sizeof(buffer));
        if (r == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        /* Ensure buffer is 0-terminated.  */

        buffer[sizeof(buffer) - 1] = 0;

        printf("Result = %s\n", buffer);

        /* Close socket.  */

        close(data_socket);

        exit(EXIT_SUCCESS);
    }

للحصول على أمثلة لاستخدام SCM_RIGHTS، انظر cmsg(3) و seccomp_unotify(2).

انظر أيضًا
recvmsg(2), sendmsg(2), socket(2), socketpair(2), cmsg(3), capabilities(7), credentials(7), socket(7), udp(7)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.