UNIX(7) | Miscellaneous Information Manual | UNIX(7) |
unix – Sockets pour communications locales entre processus
#include <sys/socket.h> #include <sys/un.h>
unix_socket = socket(AF_UNIX, type, 0); error = socketpair(AF_UNIX, type, 0, int *sv);
La famille de sockets AF_UNIX (aussi connue sous le nom de AF_LOCAL) sert à communiquer efficacement entre processus sur la même machine. Traditionnellement, les sockets de domaine UNIX peuvent ne pas être nommés ou bien être liés à un chemin d'accès de système de fichiers, lequel sera marqué comme étant de type socket. Linux gère également un espace de noms abstrait, indépendant du système de fichiers.
Les types de sockets valables dans le domaine UNIX sont : SOCK_STREAM pour un socket orienté flux et SOCK_DGRAM pour un socket orienté datagramme qui préserve les limites entre messages (comme sur la plupart des implémentations UNIX, les sockets datagramme de domaine UNIX sont toujours fiables et ne réordonnent pas les datagrammes), et (depuis Linux 2.6.4) SOCK_SEQPACKET pour un socket orienté connexion, préservant les limites entre messages et délivrant les messages dans l'ordre où ils ont été envoyés.
Les sockets de domaine UNIX prennent en charge la transmission de descripteurs de fichier ou d'accréditations d'un processus à l'autre en utilisant des données annexes.
Une adresse de socket de domaine UNIX est représentée dans la structure suivante :
struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* Chemin d’accès */ };
The sun_family field always contains AF_UNIX. On Linux, sun_path is 108 bytes in size; see also BUGS, below.
Divers appels système (par exemple, bind(2), connect(2) et sendto(2)) prennent un argument sockaddr_un en entrée. D’autres appels système (par exemple, getsockname(2), getpeername(2), recvfrom(2) et accept(2)) renvoient un argument de ce type.
Trois types d’adresse sont remarquables dans la structure sockaddr_un :
offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
Lors de la liaison d’un socket à un chemin, quelques règles doivent être observées pour une portabilité maximale et une facilité de codage :
offsetof(struct sockaddr_un, sun_path)+strlen(addr.sun_path)+1
Il y a quelques variations dans la façon dont les implémentations gèrent les adresses de socket de domaine UNIX qui ne suivent pas les règles ci-dessus.Par exemple, quelques implémentations (mais pas toutes) ajoutent un octet NULL final si aucun n’est présent dans le sun_path fourni.
Lors du codage d’applications portables, il faut penser que certaines implémentations ont un sun_path aussi court que 92 octets.
Divers appels système (accept(2), recvfrom(2), getsockname(2), getpeername(2)) renvoient les structures d’adresse de socket. Lorsque appliqué à des sockets de domaine UNIX, l’argument « value-result » addrlen fourni à l’appel devrait être initialisé comme ci-dessus. Au renvoi, l’argument est réglé pour indiquer la taille réelle de la structure d’adresse. L’appelant devrait vérifier la valeur renvoyée dans cet argument. Si la valeur de sortie excède la valeur d’entrée, alors il n’y a aucune garantie qu’un octet NULL final soit présent dans sun_path. (Consultez BOGUES.)
Dans l’implémentation de Linux, les sockets chemin d'accès respectent les permissions du répertoire dans lequel ils sont. La création d’un nouveau socket échoue si le processus n’a pas les permissions d’écriture et de recherche (exécution) dans le répertoire où le socket est créé.
Dans Linux, la connexion à un objet de socket flux nécessite la permission en écriture sur ce socket. De même, l’envoi d’un datagramme à un socket datagramme nécessite la permission en écriture sur ce socket. POSIX ne fait aucune déclaration sur les effets des permissions sur un fichier de socket, et sur certains systèmes (par exemple, les BSD anciens) les permissions de socket sont ignorées. Les programmes portables ne devraient pas se fier à cette fonctionnalité pour la sécurité.
Lors de la création d’un nouveau socket, le propriétaire et le groupe d’un fichier de socket sont définis selon les règles habituelles. Le fichier de socket a toutes les permissions activées, autres que celles désactivées par le processus umask(2).
Le propriétaire, le groupe et les permissions d’un socket chemin d'accès peuvent être modifiés (avec chown(2) et chmod(2)).
Les permissions de socket n’ont aucun sens pour les sockets abstraits : le processus umask(2) n’a aucun effet lors de la liaison d’un socket abstrait et modifier le propriétaire et les permissions de l’objet (avec fchown(2) et fchmod(2)) n’a aucun effet sur l’accessibilité du socket.
Les sockets abstraits disparaissent automatiquement quand toutes les références de socket ouvertes sont refermées.
L’espace de noms de sockets abstraits est une extension non portable de Linux.
Pour des raisons historiques, les options de ces sockets sont indiquées avec un type SOL_SOCKET même si elles sont spécifiques à AF_UNIX. Elles peuvent être définies avec setsockopt(2) et lues avec getsockopt(2) en indiquant SOL_SOCKET comme famille de sockets.
If a bind(2) call specifies addrlen as sizeof(sa_family_t), or the SO_PASSCRED socket option was specified for a socket that was not explicitly bound to an address, then the socket is autobound to an abstract address. The address consists of a null byte followed by 5 bytes in the character set [0-9a-f]. Thus, there is a limit of 2^20 autobind addresses. (From Linux 2.1.15, when the autobind feature was added, 8 bytes were used, and the limit was thus 2^32 autobind addresses. The change to 5 bytes came in Linux 2.3.15.)
Les paragraphes suivants décrivent des détails spécifiques aux domaines et des fonctionnalités de l'API des sockets de domaine UNIX non prises en charge sous Linux.
Les sockets de domaine UNIX ne prennent pas en charge la transmission de données hors-bande (l'indicateur MSG_OOB de send(2) et recv(2)).
L'indicateur MSG_MORE de send(2) n'est pas pris en charge sur les sockets de domaine UNIX.
Avant Linux 3.4, l'utilisation de MSG_TRUNC dans le paramètre flags de recv(2) n'était pas prise en charge par les sockets de domaine UNIX.
L'option SO_SNDBUF de socket a un effet pour les sockets de domaine UNIX, mais l’option SO_RCVBUF n'en a pas. Pour les sockets datagramme, la valeur SO_SNDBUF impose une limite supérieure à la taille des datagrammes sortants. Cette limite est calculée comme le double de la valeur de l'option, moins 32 octets utilisés par le surdébit.
Les données annexes sont envoyées et reçues en utilisant sendmsg(2) et recvmsg(2). Pour des raisons historiques, les messages annexes listés ci-dessous sont indiqués avec un type SOL_SOCKET même s'ils sont spécifiques AF_UNIX. Pour les envoyer, définissez le champ cmsg_level de la structure cmsghdr à SOL_SOCKET et le champ cmsg_type au type. Pour plus de détails, consultez cmsg(3).
struct ucred { pid_t pid; /* PID processus émetteur */ uid_t uid; /* UID processus émetteur */ gid_t gid; /* GID processus émetteur */ };
Lors de l’envoi des données annexes avec sendmsg(2), seul un élément de chacun des types ci-dessus peut être inclus dans le message envoyé.
Au moins un octet des données réelles doit être envoyé lors de l’envoi des données annexes. Sur Linux, cela est nécessaire pour envoyer avec succès les données annexes sur un socket flux de domaine UNIX. Lors de l’envoi des données annexes à travers un socket datagramme de domaine UNIX, il n’est pas nécessaire sur Linux d’envoyer en accompagnement une donnée quelconque réelle. Cependant, les applications portables devraient aussi inclure au moins un octet des données réelles lors de l’envoi de données annexes à travers un socket datagramme.
Lors de la réception à partir d’un socket flux, les données annexes forment une sorte de barrière pour les données reçues. Par exemple, en supposant que l’émetteur transmet comme suit :
En supposant que le récepteur réalise maintenant des appels recvmsg(2) avec chacun une taille de tampon de 20 octets, le premier appel recevra 5 octets de données, avec les données annexes envoyées par le second appel sendmsg(2). Le prochain appel recevra les 4 octets de données restants.
Si l’espace alloué pour recevoir les données annexes entrantes est trop petit, alors ces données sont tronquées au nombre d’en-têtes qui peuvent loger dans le tampon fourni (ou, dans le cas de liste de descripteurs de fichier SCM_RIGHTS, cette liste peut être tronquée). Si aucun tampon n’est fourni pour les données annexes entrantes (c’est-à-dire si le champ msg_control de la structure msghdr fourni à recvmsg(2) est NULL), alors les données annexes entrantes sont ignorées. Dans les deux cas, l’indicateur MSG_CTRUNC sera réglé dans la valeur msg.msg_flags renvoyée par recvmsg(2).
Les appels ioctl(2) suivants renvoient des informations dans value. La syntaxe correcte est :
int value; error = ioctl(unix_socket, ioctl_type, &value);
ioctl_type peut être :
D'autres erreurs peuvent être déclenchées par la couche générique de socket ou par le système de fichiers lors de la génération d’un objet socket de système de fichiers. Consultez les pages de manuel correspondantes pour plus de détails.
SCM_CREDENTIALS et l'espace de noms abstrait ont été introduits avec Linux 2.2 et ne doivent pas être utilisés dans des programmes portables. (Certains systèmes dérivés de BSD prennent aussi en charge le passage d'accréditations, mais les détails d'implémentation diffèrent).
Lier un socket avec un nom de fichier crée un socket dans le système de fichiers que l’appelant doit détruire lorsqu'il n'est plus utile (en utilisant unlink(2)). La sémantique habituelle la plus proche d’UNIX s'applique ; le socket peut être délié à tout moment et sera finalement supprimé du système de fichiers lorsque sa dernière référence sera fermée.
To pass file descriptors or credentials over a SOCK_STREAM socket, you must send or receive at least one byte of nonancillary data in the same sendmsg(2) or recvmsg(2) call.
Les sockets flux de domaine UNIX ne prennent pas en charge la notion de données hors-bande.
Lors de la liaison d’un socket à une adresse, Linux est une des implémentations qui ajoute un octet NULL final si aucun n’est fourni dans sun_path. Dans la plupart des cas cela ne pose aucun problème : quand l’adresse du socket est récupérée, elle sera plus grande d’un octet que celle fournie lors de la liaison du socket. Cependant, il existe un cas où un comportement déroutant peut se produire : si 108 octets non NULL sont fournis quand un socket est lié, alors l’ajout de l’octet NULL final incorpore la longueur du nom de chemin au-delà de sizeof(sun_path). Par conséquent, lors de la récupération de l’adresse du socket (par exemple, à l’aide de accept(2)), si l’argument addrlen de l’entrée pour l’appel de récupération est indiqué comme sizeof(struct sockaddr_un), alors la structure d’adresse renvoyée n’aura pas d’octet NULL final dans sun_path.
De plus, quelques implémentations n’ont pas besoin d’octet NULL final lors de liaison d’un socket (l’argument addrlen est utilisé pour déterminer la taille de sun_path) et lorsqu’une adresse de socket est récupérée sur ces implémentations, il n’y a pas d’octet NULL final dans sun_path.
Les applications qui récupèrent les adresses de socket peuvent coder (de manière portable) pour gérer la possibilité d’absence d’octet NULL final dans sun_path en respectant le fait que le nombre d’octets autorisés dans le nom de chemin est :
strnlen(addr.sun_path, addrlen - offsetof(sockaddr_un, sun_path))
Sinon, une application peut récupérer l’adresse de socket en allouant un tampon de taille sizeof(struct sockaddr_un)+1 mis à zéro avant la récupération. L’appel de récupération peut préciser addrlen comme sizeof(struct sockaddr_un) et l’octet zéro supplémentaire assure qu’il y aura un octet NULL final dans la chaîne renvoyée dans sun_path :
void *addrp; addrlen = sizeof(struct sockaddr_un); addrp = malloc(addrlen + 1); if (addrp == NULL) /* Gérer l’erreur */ ; memset(addrp, 0, addrlen + 1); if (getsockname(sfd, (struct sockaddr *) addrp, &addrlen)) == -1) /* Gérer l’erreur */ ; printf("sun_path = %s\n", ((struct sockaddr_un *) addrp)->sun_path);
Cette sorte de désordre peut être évité s’il est garanti que les applications qui créent les sockets de chemin suivent les règles exposées ci-dessus dans Sockets chemin d’accès.
Le code suivant démontre l’utilisation de sockets de paquets ordonnés pour une communication inter-processus locale. Il est constitué de deux programmes. Le programme serveur attend une connexion d’un programme client. Le client envoie chacun de ses arguments de ligne de commande dans des messages séparés. Le serveur traite les messages entrants comme des entiers et fait leur somme. Le client envoie la chaîne de commande « END ». Le serveur renvoie un message contenant la somme des entiers du client. Le client affiche la somme et quitte. Le serveur attend la connexion d’un nouveau client. Pour stopper le serveur, le client est appelé avec l’argument de ligne de commande « DOWN ».
La sortie suivante a été enregistrée alors que le serveur fonctionnait en arrière-plan et en exécutant le client de façon répétée. L’exécution du programme du serveur se termine quand il reçoit la commande « DOWN ».
$ ./server & [1] 25887 $ ./client 3 4 Result = 7 $ ./client 11 -5 Result = 6 $ ./client DOWN Result = 0 [1]+ Done ./server $
/* * Fichier connection.h */ #define SOCKET_NAME "/tmp/9Lq7BNBnBycd6nxy.socket" #define BUFFER_SIZE 12 /* * Fichier server.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include "connection.h" int main(int argc, char *argv[]) { struct sockaddr_un name; int down_flag = 0; int ret; int connection_socket; int data_socket; int result; char buffer[BUFFER_SIZE]; /* Création du socket local. */ connection_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (connection_socket == -1) { perror("socket"); exit(EXIT_FAILURE); } /* * Pour la portabilité effacer la structure entière, puisque * quelques implémentations ont des champs (non standard) * supplémentaires dans la structure. */ memset(&name, 0, sizeof(name)); /* Liaison du socket au nom de socket. */ 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); } /* * Préparation à accepter les connexions. La taille de réserve * est réglée à 20. Aussi tandis qu’une requête est traitée, * d’autres peuvent être en attente. */ ret = listen(connection_socket, 20); if (ret == -1) { perror("listen"); exit(EXIT_FAILURE); } /* Ceci est la boucle principale pour gérer les connexions. */ for (;;) { /* Attendre des connexions entrantes. */ data_socket = accept(connection_socket, NULL, NULL); if (data_socket == -1) { perror("accept"); exit(EXIT_FAILURE); } result = 0; for (;;) { /* Attendre le prochain paquet de données. */ ret = read(data_socket, buffer, sizeof(buffer)); if (ret == -1) { perror("read"); exit(EXIT_FAILURE); } /* Assurer que le tampon soit terminé par NULL. */ buffer[sizeof(buffer) - 1] = 0; /* Gérer les commandes. */ if (!strncmp(buffer, "DOWN", sizeof(buffer))) { down_flag = 1; break; } if (!strncmp(buffer, "END", sizeof(buffer))) { break; } /* Faire la somme des termes reçus. */ result += atoi(buffer); } /* Envoyer le résultat. */ sprintf(buffer, "%d", result); ret = write(data_socket, buffer, sizeof(buffer)); if (ret == -1) { perror("write"); exit(EXIT_FAILURE); } /* Fermer le socket. */ close(data_socket); /* Quitter avec la commande DOWN. */ if (down_flag) { break; } } close(connection_socket); /* Délier le socket. */ unlink(SOCKET_NAME); exit(EXIT_SUCCESS); } /* * Fichier client.c */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include "connection.h" int main(int argc, char *argv[]) { struct sockaddr_un addr; int ret; int data_socket; char buffer[BUFFER_SIZE]; /* Création du socket local. */ data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (data_socket == -1) { perror("socket"); exit(EXIT_FAILURE); } /* * Pour la portabilité effacer la structure entière, puisque * quelques implémentations ont des champs (non standard) * supplémentaires dans la structure. */ memset(&addr, 0, sizeof(addr)); /* Connecter le socket à l’adresse de socket. */ 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, "Le serveur est arrêté.\n"); exit(EXIT_FAILURE); } /* Envoyer les arguments. */ for (size_t i = 1; i < argc; ++i) { ret = write(data_socket, argv[i], strlen(argv[i]) + 1); if (ret == -1) { perror("write"); break; } } /* Résultat de la requête. */ strcpy(buffer, "END"); ret = write(data_socket, buffer, strlen(buffer) + 1); if (ret == -1) { perror("write"); exit(EXIT_FAILURE); } /* Recevoir le résultat. */ ret = read(data_socket, buffer, sizeof(buffer)); if (ret == -1) { perror("read"); exit(EXIT_FAILURE); } /* Assurer que le tampon soit terminé par NULL. */ buffer[sizeof(buffer) - 1] = 0; printf("Result = %s\n", buffer); /* Fermer le socket. */ close(data_socket); exit(EXIT_SUCCESS); }
For examples of the use of SCM_RIGHTS, see cmsg(3) and seccomp_unotify(2).
recvmsg(2), sendmsg(2), socket(2), socketpair(2), cmsg(3), capabilities(7), credentials(7), socket(7), udp(7)
La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Paul Guillonneau <guillonneau.jeanpaul@free.fr>
Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.
10 février 2023 | Pages du manuel de Linux 6.03 |