L'envoi de descripteur de fichier Linux, le socket
J'essaie d'envoyer un fichier descripteur de linux, le socket, mais il ne fonctionne pas. Ce que je fais mal? Comment est-on censé debug quelque chose comme cela? J'ai essayé de mettre perror() partout où c'est possible, mais ils ont affirmé que tout est ok. Voici ce que j'ai écrit:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
void wyslij(int socket, int fd) //send fd by socket
{
struct msghdr msg = {0};
char buf[CMSG_SPACE(sizeof fd)];
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof fd);
*((int *) CMSG_DATA(cmsg)) = fd;
msg.msg_controllen = cmsg->cmsg_len; //why does example from man need it? isn't it redundant?
sendmsg(socket, &msg, 0);
}
int odbierz(int socket) //receive fd from socket
{
struct msghdr msg = {0};
recvmsg(socket, &msg, 0);
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
unsigned char * data = CMSG_DATA(cmsg);
int fd = *((int*) data); //here program stops, probably with segfault
return fd;
}
int main()
{
int sv[2];
socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
int pid = fork();
if (pid > 0) //in parent
{
close(sv[1]);
int sock = sv[0];
int fd = open("./z7.c", O_RDONLY);
wyslij(sock, fd);
close(fd);
}
else //in child
{
close(sv[0]);
int sock = sv[1];
sleep(0.5);
int fd = odbierz(sock);
}
}
Je voulais dire
Pourquoi êtes-vous en envoyant le valeur d'un descripteur de fichier sur une socket? Qu'est-ce que le récepteur censé faire avec elle? Si le récepteur n'est pas dans le même processus que l'expéditeur, le descripteur de fichier ne signifie rien pour le récepteur, comme un descripteur de fichier est spécifique au procédé. En tout cas, sur l'extrémité de réception, lors de l'utilisation de
Juste pour vérifier - vous êtes conscient qu'il est possible d'envoyer (utilisable) de descripteurs de fichiers entre les processus à l'aide de
Je retire mon commentaire au sujet de l'envoi de la valeur d'un descripteur de fichier dans les limites du processus étant non valide. Mais mon commentaire sur la nécessité de numériser correctement reçu
FWIW, Il n'est pas spécifique à Linux (beaucoup, bien que je ne crois pas tout, Unix saveurs de soutien). Mais je suis d'accord que la numérisation de la
CMSG_DATA
je ne sais pas ce qu'ils sont, mais je pense que j'ai trouvé le problème.Pourquoi êtes-vous en envoyant le valeur d'un descripteur de fichier sur une socket? Qu'est-ce que le récepteur censé faire avec elle? Si le récepteur n'est pas dans le même processus que l'expéditeur, le descripteur de fichier ne signifie rien pour le récepteur, comme un descripteur de fichier est spécifique au procédé. En tout cas, sur l'extrémité de réception, lors de l'utilisation de
recvmsg()
, vous avez besoin d'une boucle sur le message reçu en-têtes à la recherche pour le SOL_SOCKET/SCM_RIGHTS
en-tête. Maintenant, vous êtes en supposant que chaque message commence par SCM_RIGHTS
, et qui ne risque pas d'être toujours le cas.Juste pour vérifier - vous êtes conscient qu'il est possible d'envoyer (utilisable) de descripteurs de fichiers entre les processus à l'aide de
SCM_RIGHTS
et AF_UNIX
sockets?Je retire mon commentaire au sujet de l'envoi de la valeur d'un descripteur de fichier dans les limites du processus étant non valide. Mais mon commentaire sur la nécessité de numériser correctement reçu
recvmsg()
en-têtes pour SCM_RIGHTS
s'applique toujours.FWIW, Il n'est pas spécifique à Linux (beaucoup, bien que je ne crois pas tout, Unix saveurs de soutien). Mais je suis d'accord que la numérisation de la
recvmsg()
des en-têtes est la meilleure pratique 🙂OriginalL'auteur Dekakaruk | 2015-01-17
Vous devez vous connecter pour publier un commentaire.
Stevens (et al) UNIX® de la Programmation Réseau, Vol 1: Les Sockets Réseau API décrit le processus de transfert des descripteurs de fichiers entre les processus dans le Chapitre 15 de Domaine Unix Protocoles et plus précisément §15.7 Passant Descripteurs. C'est fastidieux à décrire dans leur intégralité, mais il doit être fait sur une socket de domaine Unix (
AF_UNIX
ouAF_LOCAL
), et le processus émetteur utilisesendmsg()
tandis que le récepteur utiliserecvmsg()
.J'ai eu ce légèrement modifié (et instrumenté) version du code à partir de la question de travailler pour moi sur Mac OS X 10.10.1 Yosemite avec GCC 4.9.1:
La sortie de l'instrumentation, mais non, la version du code d'origine était:
Noter que le parent terminé avant l'enfant, de sorte que l'invite est apparu dans le milieu de la sortie.
La sortie de la "fixe" le code était:
La primaire des changements importants ont été l'ajout de la
struct iovec
les données dans lestruct msghdr
dans les deux fonctions, et de donner de l'espace dans la fonction de réception (odbierz()
) pour le message de contrôle. Je l'ai signalé une étape intermédiaire dans le débogage où j'ai fourni lestruct iovec
pour le parent et le parent "message trop long d'erreur" a été supprimé. Pour prouver que c'était bien de travail (un descripteur de fichier qui a été passé), j'ai ajouté le code pour lire et imprimer le fichier à partir du passé descripteur de fichier. Le code d'origine avaitsleep(0.5)
mais depuissleep()
prend un entier non signé, cela équivaut à ne pas dormir. J'ai utilisé C99 composé littéraux à l'enfant de dormir pendant au moins 0,5 seconde. La mère dort pendant 1,5 secondes, de sorte que la sortie de l'enfant est terminée avant que le parent quitte. Je pourrais utiliserwait()
ouwaitpid()
trop, mais il est trop paresseux pour le faire.Je n'ai pas rentré et vérifié que tous les ajouts ont été nécessaires.
La
"stderr.h"
en-tête déclare leerr_*()
fonctions. C'est le code que j'ai écrit (première version avant 1987) pour signaler les erreurs de manière succincte. Leerr_setlogopts(ERR_PID)
indicatifs d'appel tous les messages avec le PID. Pour l'horodatage trop,err_setlogopts(ERR_PID|ERR_STAMP)
pour faire le travail.Problèmes d'alignement
Nominale Des Animaux suggère dans un commentaire:
Et pas seulement les architectures Linux: les deux SPARC et de Pouvoir exiger des données alignées et souvent exécuter Solaris et AIX respectivement. Once upon a time, DEC Alpha nécessaire que trop, mais ils sont rarement vus dans le champ de ces jours.
Le code dans la page de manuel
gsmc(3)
est lié à ce:L'affectation à
fdptr
semble supposer queCMSG_DATA(cmsg)
est suffisamment bien alignés pour être converti enint *
et lamemcpy()
est utilisé sur l'hypothèse queNUM_FD
n'est pas seulement 1. Cela étant dit, il est censé être orientés vers le tableaubuf
, et qui pourrait ne pas être suffisamment bien alignés Nominal des Animaux suggère, donc, il me semble que lefdptr
est juste un intrus et il serait mieux si l'exemple utilisé:Et le processus inverse sur la fin de réception serait alors approprié. Ce programme ne fait que passer d'un seul descripteur de fichier, de sorte que le code est modifiable:
Étant donné que Mac OS X (qui a un Darwin/BSD) nécessite au moins un
struct iovec
, même si cela décrit un zéro-longueur du message, je suis disposé à croire que le code indiqué ci-dessus, qui comprend un 3 octets message, est un bon pas dans la bonne direction. Le message devrait peut-être être un seul octet null au lieu de 3 lettres.J'ai révisé le code pour lire comme illustré ci-dessous. Il utilise
memmove()
pour copier le fichier de descripteur et de lacmsg
tampon. Il transfère un message unique octet, qui est un octet nul.Il a également le processus parent de lecture (jusqu'à) 32 octets du fichier avant de transmettre le fichier descripteur de l'enfant. L'enfant continue de lecture où le parent a laissé. Cela démontre que le descripteur de fichier transféré comprend l'offset du fichier.
Le récepteur doit faire plus de validation sur le
cmsg
avant de le traiter comme un descripteur de fichier de passage de message.Et un exemple d'exécution:
Content que cela fonctionne pour vous. Sur Mac OS X, il semble que le
struct iovec
les parties sont nécessaires, mais l'envoi de la version peut êtrestruct iovec io = { .iov_base = "", .iov_len = 0 };
et c'est OK. La réception de l'un a travaillé avecchar mbuffer[1];
, mais pas sans lesstruct iovec
.Puis-je vous suggérer de modifier le code à copier le descripteur de type int à l'aide de
memcpy()
au lieu d'accéder aux données directement? Il n'est pas nécessairement alignées correctement, d'où l'importance de l'homme page exemple utilise égalementmemcpy()
-- et il y a de nombreuses architectures Linux où non alignés int accès provoque des problèmes (jusqu'à SIGBUS signal de tuer le processus). J'ai aussi semblent rappeler questions historiques sur divers Systèmes d'exploitation wrt. les données auxiliaires avec pas normal données de la charge utile, à éviter, par l'envoi d'au moins un mannequin octet trop, mais je ne peux pas trouver toutes les références à vérifier, donc je pourrais rappeler mal.Merci de voir mon jour de réponse. L'exemple dans la page de man est un peu déroutant avec son (inutile et peu fiable) utilisation de
fdptr
. Je pense que ce que j'ai écrit va travailler n'importe où qui prend en charge les mécanismes de base (mais j'avoue que mon test est très limité). J'utilisememmove()
au lieu dememcpy()
parce que je ne devrais pas avoir à vous soucier de savoir si les choses se chevauchent. Parce quememmove()
est garanti pour toujours etmemcpy()
n'est pas garanti de toujours fonctionner ('travail', qui signifie 'fonctionner correctement quel que soit les arguments passés"), j'utilisememmove()
. C'est un personnel politique de la décision.Le iovec données semble nécessaire que la note suivante à partir de la page de man (unix(7)) stipule: "afin De passer les descripteurs de fichier ou les informations d'identification sur une SOCK_STREAM, vous avez besoin d'envoyer ou de recevoir au moins un octet de nonancillary de données dans le même sendmsg(2) ou recvmsg(2) l'appel".
OriginalL'auteur Jonathan Leffler