Corrigés exercices
Un article de HurdFr_Wiki.
Cet article est une ébauche. Cela signifie qu'il est n'est pas considéré par son auteur comme terminé.
Vous êtes libre de l'améliorer et de retirer cet avertissement si vous jugez que l'article est maintenant finalisé.
Vous trouverez ici les corrigés des exercices proposés sur la page d'exercices. Le but est de fournir un ou plusieurs codes de solution mais aussi et surtout une explication détaillée des solutions (notamment avec d'abondants commentaires).
Il pourrait être intéressant de donner également des pistes avant le corrigé.
Sommaire |
Corrections
IPC sans MiG
Solutions
/* Hello world à la mach ipc.
Copyright (C) 2002 Neal H. Walfield <neal@cs.uml.edu>
This is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program has been distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111,
USA. */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <cthreads.h>
#include <mach.h>
struct message
{
mach_msg_header_t header;
mach_msg_type_t type;
char data[256];
};
any_t server (any_t arg)
{
mach_port_t port = *(mach_port_t *) arg;
int err;
struct message message;
message.header.msgh_local_port = port;
message.header.msgh_size = sizeof (message);
while (1)
{
/* Receive a message. */
err = mach_msg (&message.header, /* The header */
MACH_RCV_MSG, /* Flags */
0, sizeof (message), /* Send/Max receive size */
port, /* Receive port */
MACH_MSG_TIMEOUT_NONE, /* No timeout */
MACH_PORT_NULL); /* No notification */
if (err)
error (1, err, "mach_msg");
/* Verify the type tag */
assert (message.type.msgt_name == MACH_MSG_TYPE_STRING);
/* Copy the contents so that we can create a reply string. */
{
char *temp = strdup (message.data);
sprintf (message.data, "Hello %s", temp);
free (temp);
}
/* Fix the header. */
message.header.msgh_local_port = MACH_PORT_NULL;
/* Fix the type header. */
message.type.msgt_size += strlen ("Hello ") * 8;
/* Send the reply. */
err = mach_msg (&message.header, /* The message header */
MACH_SEND_MSG, /* Flags */
sizeof (message), 0, /* Send/Max receive size */
MACH_PORT_NULL, /* Receive port */
MACH_MSG_TIMEOUT_NONE, /* No timeout */
MACH_PORT_NULL); /* No notification */
if (err)
error (2, err, "mach_msg");
}
return 0;
}
int main (int argc, char *argv[])
{
mach_port_t port;
mach_port_t receive;
int err;
/* Allocate a port. */
err = mach_port_allocate (mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if (err)
error (1, err, "mach_port_allocate");
err = mach_port_allocate (mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &receive);
if (err)
error (1, err, "mach_port_allocate");
/* Create a new thread to wait for the message */
cthread_detach (cthread_fork (server, (void *) &port));
/* Send a message down the port */
while (1)
{
struct message message;
printf ("Enter your name: ");
fgets (message.data, sizeof (message.data) - 1, stdin);
if (feof (stdin))
break;
message.header.msgh_bits
= MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND,
MACH_MSG_TYPE_MAKE_SEND_ONCE);
message.header.msgh_remote_port = port; /* Request port */
message.header.msgh_local_port = receive; /* Reply port */
message.header.msgh_id = 0; /* Message id */
message.header.msgh_size = sizeof (message); /* Message size */
message.type.msgt_name = MACH_MSG_TYPE_STRING; /* Parameter type */
message.type.msgt_size = 8 * (strlen (message.data) + 1); /* # Bits */
message.type.msgt_number = 1; /* Number of elements */
message.type.msgt_inline = TRUE; /* Inlined */
message.type.msgt_longform = FALSE; /* Shortform */
message.type.msgt_deallocate = FALSE; /* Do not deallocate */
message.type.msgt_unused = 0; /* = 0 */
/* Send the message on its way and wait for a reply. */
err = mach_msg (&message.header, /* The header */
MACH_SEND_MSG | MACH_RCV_MSG, /* Flags */
sizeof (message), /* Send size */
sizeof (message), /* Max receive Size */
receive, /* Receive port */
MACH_MSG_TIMEOUT_NONE, /* No timeout */
MACH_PORT_NULL); /* No notification */
if (err)
error (3, err, "mach_msg");
/* Verify the type tag */
assert (message.type.msgt_name == MACH_MSG_TYPE_STRING);
printf ("Server said: %s", message.data);
}
return 0;
}
/*
* Basic IPC Test using only one port and a mutex to synchronize all
* (c) Gaël "Kilobug" Le Mignot <kilobug@hurdfr.org>, 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define _GNU_SOURCE 1
#include <cthreads.h>
#include <mach.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
#include <assert.h>
static mutex_t mutex;
static mutex_t mutex2;
/* Debugging code */
#ifdef DEBUG
# define dprintf (void) printf
#else
# define dprintf(fmt,...)
#endif
/* Message we prepend to the client name when answering */
static const char greetings[] = "Hello, ";
/* Structure to hold our message */
typedef struct _hello_msg_t
{
mach_msg_header_t header;
mach_msg_type_t hello_type;
char *hello_data;
} hello_msg_t;
/* Helper functions: wrapper on mach_msg to send or receive a message */
static mach_msg_return_t
mach_msg_do_send(mach_msg_header_t *header)
{
return mach_msg (header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
static mach_msg_return_t
mach_msg_do_receive(mach_msg_header_t *header)
{
return mach_msg (header, MACH_RCV_MSG, 0, header->msgh_size,
header->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
/* Helper function: fill the message header and fields */
static void
fill_send_message(hello_msg_t *hello, char *string, mach_port_t target)
{
/* Check input parameters */
assert (hello);
assert (string);
/* Fill the message header */
hello->header.msgh_size = sizeof *hello;
hello->header.msgh_remote_port = target;
hello->header.msgh_seqno = 0; /* Field ignored on sent messages */
hello->header.msgh_id = 42; /* Field with no meaning for us */
/* Fill the mach_msg_type_t for the welcome message */
hello->hello_type.msgt_name = MACH_MSG_TYPE_STRING;
hello->hello_type.msgt_size = strlen (string) + 1;
hello->hello_type.msgt_number = 1;
hello->hello_type.msgt_inline = FALSE;
hello->hello_type.msgt_longform = FALSE;
hello->hello_type.msgt_deallocate = FALSE;
hello->hello_type.msgt_unused = 0;
/* String to send */
hello->hello_data = string;
}
/*
Create a send right from a receive right
For that, we use the special mach_msg, with a receive right at target
port. For more informations, look into the Mach documentation.
*/
static mach_port_t
make_send_right (mach_port_t receive_right, unsigned int type)
{
kern_return_t ret;
typedef struct _port_msg_t
{
mach_msg_header_t header;
mach_msg_type_t right_type;
mach_port_t port;
} port_msg_t;
port_msg_t msg;
dprintf ("GLO: making send right.\n");
/* Fill the message header */
msg.header.msgh_size = sizeof msg;
msg.header.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_BITS(type, 0);
msg.header.msgh_remote_port = receive_right;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_seqno = 0; /* Field ignored on sent messages */
msg.header.msgh_id = 42; /* Field with no meaning for us */
/* Fill the mach_msg_type_t for the welcome message */
msg.right_type.msgt_name = type;
msg.right_type.msgt_size = (sizeof (mach_port_t)) * 8;
msg.right_type.msgt_number = 1;
msg.right_type.msgt_inline = TRUE;
msg.right_type.msgt_longform = FALSE;
msg.right_type.msgt_deallocate = FALSE;
msg.right_type.msgt_unused = 0;
msg.port = receive_right;
ret = mach_msg_do_send (&(msg.header));
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "E: mach_msg_do_send failed");
dprintf ("GLO: make_send_right sent.\n");
msg.header.msgh_local_port = receive_right;
ret = mach_msg_do_receive (&(msg.header));
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "E: mach_msg_do_receive failed");
return msg.port;
}
/* Main server code - takes the receive right of the port as parameter */
static void
do_server(mach_port_t server_port)
{
char *client_name = NULL;
char *answer = NULL;
mach_port_t reply_port;
hello_msg_t hello;
kern_return_t ret;
dprintf ("SRV: Hello, I'm the server.\n");
/* Receive message */
mutex_lock (mutex);
mutex_unlock (mutex2);
hello.header.msgh_size = sizeof hello;
hello.header.msgh_local_port = server_port;
ret = mach_msg_do_receive (&(hello.header));
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "SRV: E: mach_msg_receive failed");
/* Fetch data */
client_name = hello.hello_data;
printf("SRV: Receiving message: [%s]\n", client_name);
reply_port = make_send_right (server_port, MACH_MSG_TYPE_MAKE_SEND_ONCE);
mutex_unlock (mutex);
/* Create the answer string */
answer = malloc (strlen (client_name) + sizeof greetings);
if (answer == NULL)
error (1, errno, "SRV: E: malloc failed");
(void) sprintf(answer, "%s%s", greetings, client_name);
dprintf ("SRV: Will now send [%s].\n", answer);
/* Create the answer message */
fill_send_message (&hello, answer, reply_port);
hello.header.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
hello.header.msgh_local_port = MACH_PORT_NULL;
/* Send message */
ret = mach_msg_do_send (&(hello.header));
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "SRV: E: mach_msg_do_send failed");
}
/* Main client code - takes a receive right to the server as parameter */
static void
do_client(mach_port_t *port)
{
/* Get the port */
hello_msg_t hello;
mach_port_t send_right;
kern_return_t ret;
dprintf ("CLI: Hello, I'm a client.\n");
/* Create a send right on it */
send_right = make_send_right (*port, MACH_MSG_TYPE_MAKE_SEND_ONCE);
mutex_unlock (mutex);
/* Fill the message header */
fill_send_message (&hello, "ipc-mutex", send_right);
/* Fill extra fields */
hello.header.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
hello.header.msgh_local_port = MACH_PORT_NULL;
/* Send message */
ret = mach_msg_do_send (&(hello.header));
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "CLI: E: mach_msg_send failed");
dprintf ("CLI: Message sent.\n");
mutex_lock (mutex2);
mutex_lock (mutex);
/* Receive answer */
hello.header.msgh_size = sizeof(hello);
hello.header.msgh_local_port = *port;
ret = mach_msg_do_receive (&(hello.header));
mutex_unlock (mutex);
mutex_unlock (mutex2);
if (ret != MACH_MSG_SUCCESS)
error (1, ret, "CLI: E: mach_msg_receive failed");
printf("CLI: Receiving answer: [%s]\n", hello.hello_data);
}
/* Main funcion: allocate server port and start threads */
int
main(int argc, char *argv[])
{
mach_port_t server_port;
kern_return_t ret;
cthread_t client;
/* Allocate the server port */
ret = mach_port_allocate (mach_task_self (),
MACH_PORT_RIGHT_RECEIVE,
&server_port);
if (ret != KERN_SUCCESS)
error (1, ret, "E: mach_msg_receive failed");
/* Allocate and lock the two mutexes */
mutex = mutex_alloc ();
mutex_init (mutex);
mutex_lock (mutex);
mutex2 = mutex_alloc ();
mutex_init (mutex2);
mutex_lock (mutex2);
/* Fork the client */
client = cthread_fork ((cthread_fn_t) do_client, &server_port);
/* Run the server */
do_server (server_port);
/* Wait for the client */
(void) cthread_join (client);
/* Cleanly exit */
return 0;
}
IPC avec MiG
Solutions
Solution basique
Solution de l'exercice avec un nombre variable de clients, une taille maximum pour les noms de clients (256-strlen ("Hello, ") == 248)) et en n'utilisant pas libports. Écrite par Gaël `Kilobug' Le Mignot, modifiée par Manuel Menal.
/*
* Basic MIG test
* (c) Gaël "Kilobug" Le Mignot <kilobug@hurdfr.org>, 2002
* Modified by Manuel Menal <mmenal@hurdfr.org>, 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define _GNU_SOURCE 1
#include <cthreads.h>
#include <mach.h>
#include <assert.h>
#include <error.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "hello.h"
#include "helloServer.h"
/* Debugging code */
#ifdef DEBUG
# define dprintf (void) printf
#else
# define dprintf(fmt, ...)
#endif
/* Parameters given to the client thread */
typedef struct _param_t
{
mach_port_t send_right;
char **names;
int nb_names;
} param_t;
#define MAX_SIZE 512
/* Message we prepend to the client name when answering */
static const char greetings[] = "Hello, ";
/* Prototype of a MiG generated function */
extern boolean_t hello_server();
/* Our server function that will be called by MiG stubs */
kern_return_t
S_hello (mach_port_t server, mymsg_t message)
{
mymsg_t answer;
(void) printf ("SRV: Receiving message: [%s]\n", message);
/* Create the answer string */
(void) snprintf (answer, sizeof (mymsg_t), "%s%s", greetings, message);
dprintf ("SRV: Will now send [%s].\n", answer);
/* Copy it into the resulting buffer */
(void) strncpy (message, answer, sizeof (mymsg_t));
return KERN_SUCCESS;
}
/* Main server: just call MiG demuxer */
static void
server (mach_port_t port)
{
kern_return_t err;
dprintf ("SRV: Hello, I'm the server.\n");
err = mach_msg_server (hello_server, MAX_SIZE, port);
if (err != KERN_SUCCESS)
error (1, err, "mach_msg_server");
}
/* GNU C extension: we says to gcc that client will never returns */
static any_t client (any_t arg) __attribute__ ((noreturn));
/* Perform a RPC with the given input */
static void
do_client (mach_port_t send_right, const char *name)
{
kern_return_t err;
mymsg_t buf;
dprintf ("CLI: Hello, I'm the client.\n");
assert (name);
(void) strncpy (buf, name, sizeof (mymsg_t));
/* Do the RPC */
err = hello (send_right, buf);
if (err != KERN_SUCCESS)
error (1, err, "calling client stub");
(void) printf ("CLI: Received answer [%s]\n", buf);
}
/* Client thread */
static any_t
client (any_t arg)
{
/* Fetch port */
param_t *param = arg;
assert(param);
for (int i = 0; i < param->nb_names; i++)
do_client (param->send_right, param->names[i]);
exit (0);
}
int
main (int argc, char *argv[])
{
mach_port_t port;
error_t err;
param_t param;
if (argc < 2)
error (1, 0, "Usage: %s name1 [name2 ... namen]", argv[0]);
/* Allocate a port. */
err = mach_port_allocate (mach_task_self (),
MACH_PORT_RIGHT_RECEIVE, &port);
if (err)
error (1, err, "mach_port_allocate");
/* Create a send right on it */
err = mach_port_insert_right (mach_task_self (), port,
port, MACH_MSG_TYPE_MAKE_SEND);
if (err)
error (2, err, "mach_port_insert_right");
/* send_port = make_send_right (port, MACH_MSG_TYPE_MAKE_SEND);*/
/* Create a new thread to wait for the message */
param.send_right = port;
param.names = argv + 1;
param.nb_names = argc - 1;
cthread_detach (cthread_fork (client, (void *) ¶m));
/* Call the server */
server (port);
/* Clean exit */
return 0;
}
subsystem hello 42420; ServerPrefix S_; #include <mach/std_types.defs> import "hello_types.h"; type mymsg_t = (MACH_MSG_TYPE_STRING, 256*8); routine hello(server: mach_port_t; inout message: mymsg_t);
MIG = mig # i386-gnu-mig CC = gcc # i386-gnu-gcc RM = rm -f CFLAGS = -Wall -ggdb -DDEBUG -std=gnu99 LDFLAGS = -lthreads OBJS = hello.o helloServer.o helloUser.o MIGGEN = helloServer.c helloUser.c hello.h all: hello hello: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) -o $@ clean: $(RM) *~ *.o $(MIGGEN): hello.defs $(MIG) $< helloServer.h: hello.defs $(MIG) $< -sheader $@ hello.o: hello.c hello.h helloServer.h fclean: clean $(RM) $(MIGGEN) helloServer.h hello .PHONY: all clean fclean
Solution avancée
Cette solution transmet les messages en dehors du message (out of line) ce qui lui permet de ne pas imposer une limite arbitraire sur la taille du nom du client. Elle utilise également libports. Écrite par Gaël `Kilobug' Le Mignot.
/*
* Basic MIG test, using libports and Out Of Line memory, and many clients
* (c) Gaël "Kilobug" Le Mignot <kilobug@hurdfr.org>, 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define _GNU_SOURCE 1
#include <cthreads.h>
#include <mach.h>
#include <hurd/ports.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <assert.h>
#include "hello.h"
#include "helloServer.h"
/* Debugging code */
#ifdef DEBUG
# define dprintf (void) printf
#else
# define dprintf(fmt, ...)
#endif
/* Parameters given to the client thread */
typedef struct _param_t
{
mach_port_t send_right;
char **names;
int nb_names;
} param_t;
/* Message we prepend to the client name when answering */
static const char greetings[] = "Hello, ";
/* Prototype of MiG generated stub */
extern boolean_t hello_server (mach_msg_header_t *, mach_msg_header_t *);
/* Helper function: checked allocation using Mach RPC */
static vm_address_t
allocate (vm_size_t len)
{
vm_address_t res;
error_t err;
err = vm_allocate (mach_task_self (), &res, len, TRUE);
if (err != KERN_SUCCESS)
error(1, err, "vm_allocate");
return res;
}
/* Helper function: checked deallocation using Mach RPC */
static void
deallocate (vm_address_t addr, vm_size_t len)
{
error_t err;
err = vm_deallocate (mach_task_self (), addr, len);
if (err != KERN_SUCCESS)
error(1, err, "vm_deallocate");
}
/* Our server function that will be called by MiG stubs */
kern_return_t
S_hello (mach_port_t server, vm_address_t *message, vm_size_t *size)
{
vm_address_t res;
vm_size_t len;
const char *input = (char *) *message;
(void) printf ("SRV: Receiving message: [%s]\n", input);
/* Allocate output buffer */
len = *size + (sizeof greetings);
res = allocate(len);
/* Create return message */
snprintf((char *) res, len, "%s%s", greetings, input);
dprintf ("SRV: Will now send [%s].\n", (const char *) res);
/* Free input buffer */
deallocate(*message, *size);
/* Feed data */
*size = len;
*message = res;
return KERN_SUCCESS;
}
/* Perform a RPC with the given input */
static void
do_client (mach_port_t send_right, const char *name)
{
vm_address_t res, input;
kern_return_t err;
vm_size_t len, rlen;
dprintf ("CLI: Hello, I'm the client.\n");
assert (name);
len = strlen (name) + 1;
/* Allocate input buffer */
input = allocate (len);
/* Copy name on it */
(void) strncpy ((char *) input, name, len);
/* Call server RPC */
rlen = len;
res = input;
err = hello (send_right, &res, &rlen);
if (err != KERN_SUCCESS)
error (1, err, "calling client stub");
/* Print message */
(void) printf ("CLI: Received answer [%s]\n", (const char *) res);
/* Free result */
deallocate (res, rlen);
}
/* GNU C extension: we says to gcc that client will never returns */
static any_t client (any_t arg) __attribute__ ((noreturn));
/* Do our call and then exit */
static any_t
client (any_t arg)
{
param_t *param = arg;
assert(param);
for (int i = 0; i < param->nb_names; i++)
do_client (param->send_right, param->names[i]);
/* Bye, bye */
exit (0);
}
int
main (int argc, char *argv[])
{
error_t err;
struct port_class *class = ports_create_class(NULL, NULL);
struct port_bucket *bucket = ports_create_bucket();
struct port_info *pi = NULL;
param_t param;
if (argc < 2)
{
(void) fprintf (stderr, "Syntax: %s name1 [name2 ... namen]\n", argv[0]);
exit(1);
}
/* Allocate a port. */
err = ports_create_port(class, bucket, sizeof(*pi), &pi);
if (err)
error (1, err, "ports_create_port");
/* Create a send right on it */
param.send_right = ports_get_send_right(pi);
/* Create a new thread for the client */
param.names = argv + 1;
param.nb_names = argc - 1;
cthread_detach (cthread_fork (client, ¶m));
/* Now performs the main loop */
ports_manage_port_operations_one_thread(bucket, hello_server, 0);
/* Well, this will never be reached */
return 0;
}
subsystem hello 42420; ServerPrefix S_; #include <mach/std_types.defs> import "mach/i386/vm_types.h"; type mymsg_t = ^array[] of MACH_MSG_TYPE_BYTE ctype: vm_address_t; routine hello(server: mach_port_t; inout message: mymsg_t, dealloc);
MIG = mig # i386-gnu-mig CC = gcc # i386-gnu-gcc RM = rm -f CFLAGS = -Wall -ggdb -DDEBUG -std=gnu99 LDFLAGS = -lthreads -lports OBJS = hello.o helloServer.o helloUser.o MIGGEN = helloServer.c helloUser.c hello.h all: hello hello: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) -o $@ clean: $(RM) *~ *.o $(MIGGEN): hello.defs $(MIG) $< helloServer.h: hello.defs $(MIG) $< -sheader $@ hello.o: hello.c hello.h helloServer.h fclean: clean $(RM) $(MIGGEN) helloServer.h hello .PHONY: all clean fclean
Translator fortune
Solutions
Solution basique
Cette solution ne fonctionne qu'en lecture seule et utilise la commande fortune par défaut. Elle permet cependant de spécifier une commande différente en argument. Écrite par Gaël `Kilobug' Le Mignot et modifiée par Manuel Menal.
Pour la compiler :make fortune CFLAGS=-D_FILE_OFFSET_BITS=64 LDFLAGS="-lports -ltrivfs -lfshelp"
/*
* Translator that popen() a program on each read
* (c) Gaël Le Mignot "Kilobug", 2002
* Modified by Manuel Menal <mmenal@hurdfr.org>, 2006.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define _GNU_SOURCE 1
#include <hurd/trivfs.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>
/* Trivfs hooks. */
int trivfs_fstype = FSTYPE_MISC; /* Generic trivfs server */
int trivfs_fsid = 0; /* Should always be 0 on startup */
int trivfs_allow_open = O_READ;
/* Actual supported modes: */
int trivfs_support_read = 1;
int trivfs_support_write = 0;
int trivfs_support_exec = 0;
#define DEFAULT_CMDLINE "/games/fortune"
static const char *read_cmdline;
/* May do nothing, but what ? */
void
trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
{
}
/* Say goodbye */
error_t
trivfs_goaway (struct trivfs_control *cntl, int flags)
{
exit (EXIT_SUCCESS);
}
/* A hook for us to keep track of the file descriptor state. */
typedef struct _open_t
{
off_t offs;
FILE *pipe;
enum { op_read, op_none } mode;
unsigned char *buf;
off_t bufsize;
} open_t;
/*
** Open / Close
*/
static error_t
open_hook (struct trivfs_peropen *peropen)
{
/* Check parameters */
if ((peropen->openmodes & O_EXCL) == O_EXCL)
return EEXIST;
/* We are read-only */
if ((peropen->openmodes & O_WRITE) == O_WRITE)
return EACCES;
/* Allocate memory */
open_t *op = malloc (sizeof(open_t));
if (op == NULL)
return ENOMEM;
/* Initialize the offset. */
op->offs = 0;
op->bufsize = 0;
op->buf = NULL;
peropen->hook = op;
/* Neither READ nor WRITE ? what's up ? maybe just a file_name_lookup */
if ((peropen->openmodes & O_READ) != O_READ)
{
op->mode = op_none;
op->pipe = NULL;
return 0;
}
else
{
op->mode = op_read;
op->pipe = popen (read_cmdline, "r");
}
if (op->pipe == NULL)
return errno;
return 0;
}
static void
close_hook (struct trivfs_peropen *peropen)
{
open_t *op;
op = peropen->hook;
if (op->pipe != NULL)
pclose (op->pipe);
if (op->buf != NULL)
free (op->buf);
free (peropen->hook);
}
/* If this variable is set, it is called every time a new peropen
structure is created and initialized. */
error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
/* If this variable is set, it is called every time a peropen structure
is about to be destroyed. */
void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
/*
** Read
*/
error_t
trivfs_S_io_read (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t reply_type,
data_t *data, mach_msg_type_number_t *data_len,
off_t offs, mach_msg_type_number_t amount)
{
int len;
open_t *op;
char buffer[512];
/* Deny access if they have bad credentials. */
if (!cred)
return EOPNOTSUPP;
if (!(cred->po->openmodes & O_READ))
return EBADF;
op = cred->po->hook;
if (op->mode != op_read)
return EBADF;
/* Get the offset. */
if (offs == -1)
offs = op->offs;
/* We don't have enough data in the buffer, read some of them */
while (offs + amount > op->bufsize)
{
if ((len = fread (buffer, 1, sizeof (buffer), op->pipe)) == 0)
break;
op->buf = realloc (op->buf, op->bufsize + len + 1);
if (op->buf == NULL)
{
op->bufsize = 0;
return ENOMEM;
}
memcpy (op->buf + op->bufsize, buffer, len);
op->bufsize += len;
}
if (op->buf == NULL)
return EIO;
/* Prune the amount they want to read. */
if (offs > op->bufsize)
offs = op->bufsize;
if (offs + amount > op->bufsize)
amount = op->bufsize - offs;
if (amount > 0)
{
/* Possibly allocate a new buffer. */
if (*data_len < amount)
*data = (data_t) mmap (0, amount, PROT_READ|PROT_WRITE,
MAP_ANON, 0, 0);
/* Copy the constant data into the buffer. */
memcpy ((char *) *data, op->buf + offs, amount);
/* Update the saved offset. */
op->offs += amount;
}
*data_len = amount;
return 0;
}
kern_return_t
trivfs_S_io_write (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
data_t data, mach_msg_type_number_t datalen,
off_t offs, mach_msg_type_number_t *amout)
{
return EROFS;
}
/* Tell how much data can be read from the object without blocking for
a "long time" (this should be the same meaning of "long time" used
by the nonblocking flag. */
kern_return_t
trivfs_S_io_readable (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
mach_msg_type_number_t *amount)
{
if (!cred)
return EOPNOTSUPP;
else if (!(cred->po->openmodes & O_READ))
return EINVAL;
else
*amount = 10000; /* Dummy value */
return 0;
}
/* Truncate file. */
kern_return_t
trivfs_S_file_set_size (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t reply_type,
off_t size)
{
return EROFS;
}
/* Change current read/write offset */
error_t
trivfs_S_io_seek (struct trivfs_protid *cred, mach_port_t reply,
mach_msg_type_name_t reply_type, off_t offs, int whence,
off_t *new_offs)
{
open_t *op;
/* Deny access if they have bad credentials. */
if (!cred)
return EOPNOTSUPP;
op = cred->po->hook;
if (op->mode != op_read)
return EOPNOTSUPP;
switch (whence)
{
case SEEK_SET:
op->offs = offs;
*new_offs = op->offs;
return 0;
case SEEK_CUR:
op->offs += offs;
*new_offs = op->offs;
return 0;
case SEEK_END:
return EOPNOTSUPP;
default:
return EINVAL;
}
return 0;
}
/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and
SELECT_URG. Block until one of the indicated types of i/o can be
done "quickly", and return the types that are then available.
TAG is returned as passed; it is just for the convenience of the
user in matching up reply messages with specific requests sent. */
kern_return_t
trivfs_S_io_select (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
int *type)
{
if (!cred)
return EOPNOTSUPP;
else
if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
|| ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
return EBADF;
else
*type &= ~SELECT_URG;
return 0;
}
/* Well, we have to define these four functions, so here we go: */
kern_return_t
trivfs_S_io_get_openmodes (struct trivfs_protid *cred, mach_port_t reply,
mach_msg_type_name_t replytype, int *bits)
{
if (!cred)
return EOPNOTSUPP;
else
{
*bits = cred->po->openmodes;
return 0;
}
}
error_t
trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int mode)
{
if (!cred)
return EOPNOTSUPP;
else
return EROFS;
}
kern_return_t
trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int bits)
{
if (!cred)
return EOPNOTSUPP;
else
return EROFS;
}
kern_return_t
trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int bits)
{
if (!cred)
return EOPNOTSUPP;
else
return EROFS;
}
int
main (int argc, char *argv[])
{
error_t err;
mach_port_t bootstrap;
struct trivfs_control *fsys = NULL;
/* Check if we are a translator, else display a short help */
task_get_bootstrap_port (mach_task_self (), &bootstrap);
if (bootstrap == MACH_PORT_NULL)
{
error (1, 0, "popen 'fortune' translator - "
"(c) Gaël \"kilobug\" Le Mignot, 2002\n"
"This is Free Software, with ABSOLUTELY NO WARRANTY.\n\n"
"It must be started as a translator, see settrans(1).\n"
"Usage: %s command\n", argv[0]);
}
/* Fetch arguments */
if (argc >= 2)
read_cmdline = argv[1];
else
read_cmdline = DEFAULT_CMDLINE;
/* Reply to our parent */
err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
mach_port_deallocate (mach_task_self (), bootstrap);
if (err)
error (1, err, "trivfs_startup failed");
/* Launch. */
ports_manage_port_operations_one_thread (fsys->pi.bucket,
trivfs_demuxer, 0);
return 0;
}
Solution avancée
Cette solution fonctionne en lecture/écriture, avec la possibilité de spécifier une commande pour la lecture et une pour l'écriture. Écrite par Gaël `Kilobug' Le Mignot et modifiée par Manuel Menal.
Exemple d'utilisation :
$ settrans -ca .signature ./fortune-rw /games/fortune "/bin/cat > .sigstatic" $ echo "Boring, default signature" > .signature $ cat .sigstatic Boring, default signature $ cat .signature We just joined the civil hair patrol!Pour la compiler :
make fortune CFLAGS=-D_FILE_OFFSET_BITS=64 LDFLAGS="-lports -ltrivfs -lfshelp"
/*
* Translator that popen() a program on each read/write
* (c) Gaël Le Mignot "Kilobug", 2002
* Modified by Manuel Menal <mmenal@hurdfr.org>, 2006.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#define _GNU_SOURCE 1
#include <hurd/trivfs.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>
/* Trivfs hooks. */
int trivfs_fstype = FSTYPE_MISC; /* Generic trivfs server */
int trivfs_fsid = 0; /* Should always be 0 on startup */
int trivfs_allow_open = O_READ | O_WRITE;
/* Actual supported modes: */
int trivfs_support_read = 1;
int trivfs_support_write = 1;
int trivfs_support_exec = 0;
#define DEFAULT_CMDLINE "/games/fortune"
static const char *read_cmdline, *write_cmdline;
/* May do nothing, but what ? */
void
trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
{
}
/* Say goodbye */
error_t
trivfs_goaway (struct trivfs_control *cntl, int flags)
{
exit (EXIT_SUCCESS);
}
/* A hook for us to keep track of the file descriptor state. */
typedef struct _open_t
{
off_t offs;
FILE *pipe;
enum { op_read, op_write, op_none } mode;
unsigned char *buf;
off_t bufsize;
} open_t;
/*
** Open / Close
*/
static error_t
open_hook (struct trivfs_peropen *peropen)
{
/* Check parameters */
if ((peropen->openmodes & O_EXCL) == O_EXCL)
return EEXIST;
/* We are supporting READ and WRITE */
if (((peropen->openmodes & O_READ) == O_READ) &&
((peropen->openmodes & O_WRITE) == O_WRITE))
return EACCES;
/* Allocate memory */
open_t *op = malloc (sizeof(open_t));
if (op == NULL)
return ENOMEM;
/* Initialize the offset. */
op->offs = 0;
op->bufsize = 0;
op->buf = NULL;
peropen->hook = op;
/* Neither READ nor WRITE ? what's up ? maybe just a file_name_lookup */
if (((peropen->openmodes & O_READ) != O_READ) &&
((peropen->openmodes & O_WRITE) != O_WRITE))
{
op->mode = op_none;
op->pipe = NULL;
return 0;
}
if ((peropen->openmodes & O_READ) == O_READ)
{
op->mode = op_read;
op->pipe = popen (read_cmdline, "r");
}
else
{
op->mode = op_write;
op->pipe = popen (write_cmdline, "w");
}
if (op->pipe == NULL)
return errno;
return 0;
}
static void
close_hook (struct trivfs_peropen *peropen)
{
open_t *op;
op = peropen->hook;
if (op->pipe != NULL)
pclose (op->pipe);
if (op->buf != NULL)
free (op->buf);
free (peropen->hook);
}
/* If this variable is set, it is called every time a new peropen
structure is created and initialized. */
error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
/* If this variable is set, it is called every time a peropen structure
is about to be destroyed. */
void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
/*
** Read
*/
error_t
trivfs_S_io_read (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t reply_type,
data_t *data, mach_msg_type_number_t *data_len,
off_t offs, mach_msg_type_number_t amount)
{
int len;
open_t *op;
char buffer[512];
/* Deny access if they have bad credentials. */
if (!cred)
return EOPNOTSUPP;
if (!(cred->po->openmodes & O_READ))
return EBADF;
op = cred->po->hook;
if (op->mode != op_read)
return EBADF;
/* Get the offset. */
if (offs == -1)
offs = op->offs;
/* We don't have enough data in the buffer, read some of them */
while (offs + amount > op->bufsize)
{
if ((len = fread (buffer, 1, sizeof (buffer), op->pipe)) == 0)
break;
op->buf = realloc (op->buf, op->bufsize + len + 1);
if (op->buf == NULL)
{
op->bufsize = 0;
return ENOMEM;
}
memcpy (op->buf + op->bufsize, buffer, len);
op->bufsize += len;
}
if (op->buf == NULL)
return EIO;
/* Prune the amount they want to read. */
if (offs > op->bufsize)
offs = op->bufsize;
if (offs + amount > op->bufsize)
amount = op->bufsize - offs;
if (amount > 0)
{
/* Possibly allocate a new buffer. */
if (*data_len < amount)
*data = (data_t) mmap (0, amount, PROT_READ|PROT_WRITE,
MAP_ANON, 0, 0);
/* Copy the constant data into the buffer. */
memcpy ((char *) *data, op->buf + offs, amount);
/* Update the saved offset. */
op->offs += amount;
}
*data_len = amount;
return 0;
}
kern_return_t
trivfs_S_io_write (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
data_t data, mach_msg_type_number_t datalen,
off_t offs, mach_msg_type_number_t *amout)
{
open_t *op;
int written = 0;
int len;
if (!cred)
return EOPNOTSUPP;
else if (!(cred->po->openmodes & O_WRITE))
return EBADF;
op = cred->po->hook;
if (op->mode != op_write)
return EBADF;
while (written < datalen)
{
if ((len = fwrite (((char *) data) + written, 1,
datalen - written, op->pipe)) == 0)
break;
written += len;
}
*amout = written;
return 0;
}
/* Tell how much data can be read from the object without blocking for
a "long time" (this should be the same meaning of "long time" used
by the nonblocking flag. */
kern_return_t
trivfs_S_io_readable (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
mach_msg_type_number_t *amount)
{
if (!cred)
return EOPNOTSUPP;
else if (!(cred->po->openmodes & O_READ))
return EINVAL;
else
*amount = 10000; /* Dummy value */
return 0;
}
/* Truncate file. */
kern_return_t
trivfs_S_file_set_size (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t reply_type,
off_t size)
{
if (!cred)
return EOPNOTSUPP;
else if (!(cred->po->openmodes & O_WRITE))
return EINVAL;
return 0;
}
/* Change current read/write offset */
error_t
trivfs_S_io_seek (struct trivfs_protid *cred, mach_port_t reply,
mach_msg_type_name_t reply_type, off_t offs, int whence,
off_t *new_offs)
{
open_t *op;
/* Deny access if they have bad credentials. */
if (!cred)
return EOPNOTSUPP;
op = cred->po->hook;
if (op->mode != op_read)
return EOPNOTSUPP;
switch (whence)
{
case SEEK_SET:
op->offs = offs;
*new_offs = op->offs;
return 0;
case SEEK_CUR:
op->offs += offs;
*new_offs = op->offs;
return 0;
case SEEK_END:
return EOPNOTSUPP;
default:
return EINVAL;
}
return 0;
}
/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and
SELECT_URG. Block until one of the indicated types of i/o can be
done "quickly", and return the types that are then available.
TAG is returned as passed; it is just for the convenience of the
user in matching up reply messages with specific requests sent. */
kern_return_t
trivfs_S_io_select (struct trivfs_protid *cred,
mach_port_t reply, mach_msg_type_name_t replytype,
int *type)
{
if (!cred)
return EOPNOTSUPP;
else
if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
|| ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
return EBADF;
else
*type &= ~SELECT_URG;
return 0;
}
/* Well, we have to define these four functions, so here we go: */
kern_return_t
trivfs_S_io_get_openmodes (struct trivfs_protid *cred, mach_port_t reply,
mach_msg_type_name_t replytype, int *bits)
{
if (!cred)
return EOPNOTSUPP;
else
{
*bits = cred->po->openmodes;
return 0;
}
}
error_t
trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int mode)
{
if (!cred)
return EOPNOTSUPP;
else
return 0;
}
kern_return_t
trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int bits)
{
if (!cred)
return EOPNOTSUPP;
else
return 0;
}
kern_return_t
trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
mach_port_t reply,
mach_msg_type_name_t replytype,
int bits)
{
if (!cred)
return EOPNOTSUPP;
else
return 0;
}
int
main (int argc, char *argv[])
{
error_t err;
mach_port_t bootstrap;
struct trivfs_control *fsys = NULL;
/* Check if we are a translator, else display a short help */
task_get_bootstrap_port (mach_task_self (), &bootstrap);
if (bootstrap == MACH_PORT_NULL)
{
(void ) printf ("popen 'fortune' translator - "
"(c) Gaël \"kilobug\" Le Mignot, 2002\n"
"This is a Free Software, with ABSOLUTELY NO WARRANTY.\n\n"
"It must be started as a translator, see settrans(1).\n"
"Usage: %s [read_cmdline [write_cmdline]]\n", argv[0]);
exit (1);
}
/* Fetch arguments */
if (argc >= 2)
read_cmdline = argv[1];
else
read_cmdline = DEFAULT_CMDLINE;
if (argc >= 3)
write_cmdline = argv[2];
else
write_cmdline = read_cmdline;
/* Reply to our parent */
err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
mach_port_deallocate (mach_task_self (), bootstrap);
if (err)
error (1, err, "trivfs_startup failed");
/* Launch. */
ports_manage_port_operations_one_thread (fsys->pi.bucket,
trivfs_demuxer, 0);
return 0;
}

