[C] redirect stdout naar socket

Pagina: 1
Acties:

  • yiko
  • Registratie: September 2003
  • Laatst online: 20-04-2025
Momenteel ben ik bezig met een server applicatie te schrijven in C die het mogelijk moet maken om onze COBOL programma's uit te voeren. De applicatie zit al op het punt dat er verschillende processen gestart worden en op de specifieke poort luisteren, en het COBOL programma opgestart wordt bij het ontvangen van een request.

Het lastige deel was het zoeken naar een manier om stdout te kunnen redirecten, wat mij uiteindelijk gelukt is met de dup2() system call. Alles wat de COBOL routine naar stdout schrijft, komt aan de andere kant van de socket terecht. Door een close() van de client socket te doen, werd de socket wel niet effectief gesloten, omdat stdout ook nog naar diezelfde socket verwijst. Een close() doen van stdout zorgde ervoor dat de client socket wel werd gesloten. De code die ik nu heb, ziet er nu ruwweg op de volgende manier uit.
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    process_t * this = global_scoreboard[ index ]; // struct met process status,pid, etc...

    while( ! this->must_shutdown ) {
    
        this->status = PROCESS_READY;

        client = accept( server );

        close( STDOUT_FILENO );

        if( dup2( client->socket , STDOUT_FILENO ) == -1 ) {
            DWARF_ERROR( "error duplicating socket file descriptor to stdout: %s" , strerror(errno ) );
        }

        printf( "***** request processed by " __FILE__ " *****\n" );
        cobcall( "ons cobol programma" , 0 , NULL );

        this->status = PROCESS_CLOSING;

        close( client );
        close( STDOUT_FILENO );
    }

En alles verloopt perfect. Althans voor de eerste uitgevoerde request. Bij de volgende krijg ik volgende fout.
code:
1
E-error(dwarf_process.c:60 - dwarf_process_run) error duplicating socket file descriptor to stdout: Bad file number

Dit is dus een fout bij de tweede call naar dup2(). Deze fout kreeg ik niet wanneer de close(STDOUT_FILENO); er nog niet inzat.

Volgens wat ik lees in de man page van dup2(fd1,fd2), wordt fd2 een verwijzing naar fd1. Wanneer fd2 een filehandle is naar een open file, wordt deze eerst gesloten. Dus hoe je het ook draait of keert, bij de aanroep van dup2 is stdout al gesloten. Toch krijg ik bij de tweede call een fout, hoewel bij allebei stdout al gesloten is.

Zijn er mensen die hiermee ervaring hebben en mij verder op weg kunnen helpen?

  • yiko
  • Registratie: September 2003
  • Laatst online: 20-04-2025
Typisch, halve dag zitten zoeken, net de vraag gepost en 10 minuutjes later vind ik zelf het antwoord.
Oplossing is om nog ergens een copy van stdout bij te houden:
C:
1
2
3
4
5
6
7
8
9
10
11
int old_stdout;

if( ( old_stdout = dup( STDOUT_FILENO ) ) == -1 ) {
    DWARF_ERROR( "Could not copy stdout handle: %s" , strerror( errno ) );
}

// dan in de loop na de 2 close() calls 
// restore stdout
if( dup2( old_stdout , STDOUT_FILENO ) == -1 ) {
    DWARF_ERROR( "Could not restore old stdout to stdout: %s" , strerror( errno ) );
}

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 12-02 11:14
Toch klopt je uitleg niet helemaal met de foutmeldingen. Die "bad file number" zou alleen maar voor moeten kunnen komen als de socket ongeldig is (of als STDOUT_FILENO geen geldig nummer bevat maar ik neem aan dat dat gewoon een macro van 1 is?). Zoals je zelf al zegt sluit dup2() de nieuwe file descriptor wel, dus of die open is of niet maakt niet uit.

Ik snap dan ook niet helemaal waarom je oplossing het probleem oplost...

offtopic:
Je weet trouwens dat je op deze manier eigenlijk de functionaliteit van inetd aan het dupliceren bent?

[ Voor 11% gewijzigd door Soultaker op 13-10-2006 17:30 ]


  • yiko
  • Registratie: September 2003
  • Laatst online: 20-04-2025
STDOUT_FILENO is idd gewoon een macro voor 1, maar ik heb de oorzaak gevonden. De client socket krijgt de laagst beschikbare filehandle toegekend, wat bij mij de eerste keer 6 is. Bij de tweede request is stdout gesloten, waardoor filehandle 1 beschikbaar is en dus wordt de client socket filehandle 1. Hierdoor krijg je dus eigenlijk dup2(1,1) , wat foutief is.

Door met dup() een copy te nemen van stdout (wat in mijn geval 5 als filehandle geeft), kan ik dan zonder problemen stdout terug gaan linken naar filehandle 1.

Omtrent inetd: voor onze applicatie server is het belangrijk dat telkens hetzelfde proces opnieuw kan gebruikt worden, en dat dus niet voor iedere request een nieuwe proces moet geforkt worden. Of dit nu overeen komt met de manier van werken in inetd kan ik niet zeggen, omdat ik geen ervaring hiermee heb.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 12-02 11:14
yiko schreef op vrijdag 13 oktober 2006 @ 21:10:
STDOUT_FILENO is idd gewoon een macro voor 1, maar ik heb de oorzaak gevonden. De client socket krijgt de laagst beschikbare filehandle toegekend, wat bij mij de eerste keer 6 is. Bij de tweede request is stdout gesloten, waardoor filehandle 1 beschikbaar is en dus wordt de client socket filehandle 1. Hierdoor krijg je dus eigenlijk dup2(1,1) , wat foutief is.
Ah, natuurlijk! Dat is precies het soort fout wat ik vermoedde, maar niet precies m'n vinger op kon leggen.
Door met dup() een copy te nemen van stdout (wat in mijn geval 5 als filehandle geeft), kan ik dan zonder problemen stdout terug gaan linken naar filehandle 1.
Maar dat is dan helemaal overbodig. Je kunt dan alle dup-code eruit gooien en gewoon fd 1 sluiten in het begin, dan komt je socket automatisch op fd 1. (En dan je foutmeldingen allemaal naar stderr sturen dus, of toch een back-up maken van fd 1.)

Zoiets dus:
C:
1
2
3
4
5
6
7
8
    close( STDOUT_FILENO ); 
    while( ! this->must_shutdown ) { 
        client = accept( server ); 
        printf( "***** request processed by " __FILE__ " *****\n" ); 
        cobcall( "ons cobol programma" , 0 , NULL ); 
        this->status = PROCESS_CLOSING; 
        close( client ); 
    }

(Voor de duidelijkheid: dat de laagste beschikbare file descriptor gebruikt wordt is geen toeval, maar dat is een POSIX requirement. Je vertrouwt hiermee dus niet op ongedefinieerd gedrag.)
Omtrent inetd: voor onze applicatie server is het belangrijk dat telkens hetzelfde proces opnieuw kan gebruikt worden, en dat dus niet voor iedere request een nieuwe proces moet geforkt worden. Of dit nu overeen komt met de manier van werken in inetd kan ik niet zeggen, omdat ik geen ervaring hiermee heb.
inetd start inderdaad een apart proces per verbinding (je kunt wel wat configureren, maar daar kom je toch niet echt onderuit). Als je dat niet wil is een eigen wrapper schrijven wel beter. Ik wilde het alleen even genoemd hebben; voor veel dingen is inetd wél prima geschikt en dan had je je er een hoop werk mee kunnen besparen. :)

  • yiko
  • Registratie: September 2003
  • Laatst online: 20-04-2025
Soultaker schreef op vrijdag 13 oktober 2006 @ 21:17:
Maar dat is dan helemaal overbodig. Je kunt dan alle dup-code eruit gooien en gewoon fd 1 sluiten in het begin, dan komt je socket automatisch op fd 1. (En dan je foutmeldingen allemaal naar stderr sturen dus, of toch een back-up maken van fd 1.)

Zoiets dus:
C:
1
2
3
4
5
6
7
8
    close( STDOUT_FILENO ); 
    while( ! this->must_shutdown ) { 
        client = accept( server ); 
        printf( "***** request processed by " __FILE__ " *****\n" ); 
        cobcall( "ons cobol programma" , 0 , NULL ); 
        this->status = PROCESS_CLOSING; 
        close( client ); 
    }

(Voor de duidelijkheid: dat de laagste beschikbare file descriptor gebruikt wordt is geen toeval, maar dat is een POSIX requirement. Je vertrouwd hiermee dus niet op ongedefinieerd gedrag.)
De dup() code is idd overbodig, net eventjes getest. En aangezien de dup calls overbodig zijn, kan ik ze beter weglaten :) (zal wel stukje commentaar aan mijn code toevoegen met de uitleg, zodat ik het de volgende keer nog weet ;) )
In ieder geval, bedankt Soultaker

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 09:03
Is interessant onderwerp, ik heb deze functionaliteit namenlijk binnenkort ook nodig. Ik zat zelf eigenlijk te denken om op een of andere manier netcat (nc) te gebruiken maar als ik zie hoe simpel het is ga ik dit gewoon overnemen geloof ik :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1