Lancer un serveur et rendre la main

Fermé
heliconius Messages postés 539 Date d'inscription mardi 1 juillet 2008 Statut Membre Dernière intervention 23 juin 2023 - Modifié par heliconius le 3/09/2013 à 05:25
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 - 4 sept. 2013 à 10:32
Bonjour,

Je suis en train de réaliser un programme vraiment minimaliste en C mais je ne suis pas du tout expert en C.

En fait, je suis sous Win XP Pro SP3 et je veux lancer dans une fenêtre DOS (commande cmd.exe) le serveur MySQL (commande 'mysqld'). Il n'y a pas de problème sauf que... après le lancement le curseur est "gelé" et que je suis obligé de fermer la fenêtre car même un Ctrl-C ne fonctionne pas. Une fois une nouvelle fenêtre réouverte, je peux sans problème continuer à travailler mais c'est un peu pénible.

J'ai essayé de faire ça en C :

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
 system("\\bin\\mysqld"); /* exécutables sur \bin d'une clef USB */
 exit(EXIT_SUCCESS);
}

Il y a une amélioration : ça se lance et je peux faire un Ctrl-C avant de continuer.
Mais n'y a-t-il pas moyen de lancer le serveur en tâche de fond et de rendre la main pour finir ?
Je crois que sous Unix un & permettait de lancer un programme en tâche de fond.
Avez vous une solution ou une piste ? Merci

2 réponses

[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié par [Dal] le 3/09/2013 à 14:42
Salut heliconius,

1.

La façon correcte de lancer un démon sous Windows, serait de l'installer en tant que service et :

- d'utiliser "net start nomduservice" en ligne de commande pour le lancer, si tu es en ligne de commande,
- ou d'utiliser l'API Windows et sa fonction StartService, si tu programmes en C, ce qui est ton cas : https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicea?redirectedfrom=MSDN

L'usage de StartService nécessite d'inclure Windows.h et de lier ton programme à Advapi32.lib.

Ce n'est pas aussi simple...

Comme tu le vois dans le prototype de la fonction, elle n'est pas exactement autonôme, et suppose que tu aies un "handle" sur le service, que tu obtiens avec OpenService et cette fonction nécessite elle-même un handle vers un gestionnaire de contrôle de service que tu obtiens avec OpenSCManager.

Heureusement, Microsoft fournit un exemple pour s'y retrouver un peu, :

https://docs.microsoft.com/en-us/windows/win32/services/svccontrol-cpp?redirectedfrom=MSDN

2.

Tu peux vérifier les services installés, en lançant services.msc. Tu peux accéder au nom identifiant le service en ouvrant les propriétés d'un service donné. C'est ce nom que tu dois passer à OpenService.

3.

Si ton mysql n'est pas déjà installé en tant que service, tu peux l'installer en suivant les instructions figurant là :

http://dev.mysql.com/doc/refman/5.0/fr/windows-start-service.html


Dal
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié par [Dal] le 3/09/2013 à 14:53
Sinon, tu as du code intéressant dans ces autres exemples aussi :

https://docs.microsoft.com/en-us/windows/win32/services/svcconfig-cpp?redirectedfrom=MSDN
https://docs.microsoft.com/en-us/windows/win32/services/svc-cpp?redirectedfrom=MSDN

qui illustrent comment consulter le statut d'un service et, notamment, comment installer un service en C en utilisant l'API Windows


Dal

P.S. : cela dit, je n'ai jamais tenté d'installer un service sous Windows dont l'exécutable réside sur un support amovible. Je ne sais pas si cela fait une différence.
0
heliconius Messages postés 539 Date d'inscription mardi 1 juillet 2008 Statut Membre Dernière intervention 23 juin 2023 139
3 sept. 2013 à 16:06
Arf ! Je ne m'attendais pas à ça...

Merci, Dal, pour ta réponse.

En fait j'ai récupéré la partie MySQL de EasyPHP (installable sur une clef USB). je n'ai pas besoin de PHP ni de phpMyAdmin, mais juste mysql pour l'avoir sur une clef avec moi, genre clef PortableApps. Point.

J'avais déjà lu le lien que tu m'as envoyé en point n° 3 et le : "mysqld --install-manual" semble s'installer normalement. Ton message m'a fait essayer "net start mysql" mais ça me génère une erreur.

J'ai essayé :
Q:\bin>mysqld --install-manual
Service successfully installed.
puis (après un --remove) :
Q:\bin>mysqld --install mysql --defaults-file=\my.ini
Service successfully installed.

Q:\bin>net
La syntaxe de cette commande est :

NET [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |
      HELPMSG | LOCALGROUP | NAME | PAUSE | PRINT | SEND | SESSION |
      SHARE | START | STATISTICS | STOP | TIME | USE | USER | VIEW ]

Dans les deux cas d'install, ça me génère la même erreur
Q:\bin>net start mysql
Le service MySQL démarre.
Le service MySQL n'a pas pu être lancé.

Une erreur système s'est produite.
L'erreur système 1067 s'est produite.
Le processus s'est arrêté inopinément.

Q:\bin>

J'ai vu l'exemple fourni par Microsoft. Fiou ! Il faut taper tout ça ?

1° question: je ne programme pas en C. J'essaye bien des tout petits trucs mais c'est tout. J'utilise surtout PHP qui, parait-il, a une syntaxe qui pourrait être similaire au C. Mais le C pour moi est un autre monde.

2° question: pour mes bidouilles en C j'utilise Code::Block. Est-ce que ça conviendrait pour l'exemple de Microsoft adapté à mon pb ? (si j'arrive à l'adapter) ?

Merci pour ton aide.
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié par [Dal] le 3/09/2013 à 17:33
On dirait que le service est installé, mais qu'il y a une erreur à l'exécution.

C'est peut-être lié au fait que ton installation n'est pas très orthodoxe.

Essaye de voir ce que dit l'Observateur d'événements. Sous Windows XP c'est comme cela qu'on y accède : http://support.microsoft.com/kb/308427/fr

L'exemple fournit (le premier : https://docs.microsoft.com/en-us/windows/win32/services/svccontrol-cpp?redirectedfrom=MSDN) est un programme de ligne de commande permettant de faire plusieurs choses.

L'idée est se t'inspirer de la fonction DoStartSvc, qui est celle qui code l'exemple de démarrage d'un service.

Oui, Codeblocks (MinGW) peut être utilisé. Il faut adapter un peu le code et paramétrer correctement le projet Codeblocks.

Voilà ce que cela donne avec Codeblocks utilisant MinGW et une fonctionnalité limitée à lancer un service identifié par son nom dans un define :

#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#define NAMEOFSERVICE "mysql"

SC_HANDLE schSCManager;
SC_HANDLE schService;

void DoStartSvc();

int main(void)
{
    DoStartSvc();
    return 0;
}

void DoStartSvc()
{
    SERVICE_STATUS_PROCESS ssStatus;
    DWORD dwOldCheckPoint;
    DWORD dwStartTickCount;
    DWORD dwWaitTime;
    DWORD dwBytesNeeded;

    // Get a handle to the SCM database.

    schSCManager = OpenSCManager(
        NULL,                    // local computer
        NULL,                    // servicesActive database
        SC_MANAGER_ALL_ACCESS);  // full access rights

    if (NULL == schSCManager)
    {
        printf("OpenSCManager failed (%d)\n", (uint32_t)GetLastError());
        return;
    }

    // Get a handle to the service.

    schService = OpenService(
        schSCManager,         // SCM database
        NAMEOFSERVICE,              // name of service
        SERVICE_ALL_ACCESS);  // full access

    if (schService == NULL)
    {
        printf("OpenService failed (%d)\n", (uint32_t)GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }

    // Check the status in case the service is not stopped.

    if (!QueryServiceStatusEx(
            schService,                     // handle to service
            SC_STATUS_PROCESS_INFO,         // information level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // size needed if buffer is too small
    {
        printf("QueryServiceStatusEx failed (%d)\n", (uint32_t)GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    // Check if the service is already running. It would be possible
    // to stop the service here, but for simplicity this example just returns.

    if(ssStatus.dwCurrentState != SERVICE_STOPPED && ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
    {
        printf("Cannot start the service because it is already running\n");
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    // Wait for the service to stop before attempting to start it.

    while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
    {
        // Do not wait longer than the wait hint. A good interval is
        // one-tenth of the wait hint but not less than 1 second
        // and not more than 10 seconds.

        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status until the service is no longer stop pending.

        if (!QueryServiceStatusEx(
                schService,                     // handle to service
                SC_STATUS_PROCESS_INFO,         // information level
                (LPBYTE) &ssStatus,             // address of structure
                sizeof(SERVICE_STATUS_PROCESS), // size of structure
                &dwBytesNeeded ) )              // size needed if buffer is too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", (uint32_t)GetLastError());
            CloseServiceHandle(schService);
            CloseServiceHandle(schSCManager);
            return;
        }

        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                printf("Timeout waiting for service to stop\n");
                CloseServiceHandle(schService);
                CloseServiceHandle(schSCManager);
                return;
            }
        }
    }

    // Attempt to start the service.

    if (!StartService(
            schService,  // handle to service
            0,           // number of arguments
            NULL) )      // no arguments
    {
        printf("StartService failed (%d)\n", (uint32_t)GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }
    else printf("Service start pending...\n");

    // Check the status until the service is no longer start pending.

    if (!QueryServiceStatusEx(
            schService,                     // handle to service
            SC_STATUS_PROCESS_INFO,         // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
    {
        printf("QueryServiceStatusEx failed (%d)\n", (uint32_t)GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    // Save the tick count and initial checkpoint.

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
    {
        // Do not wait longer than the wait hint. A good interval is
        // one-tenth the wait hint, but no less than 1 second and no
        // more than 10 seconds.

        dwWaitTime = ssStatus.dwWaitHint / 10;

        if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
        else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

        Sleep( dwWaitTime );

        // Check the status again.

        if (!QueryServiceStatusEx(
            schService,             // handle to service
            SC_STATUS_PROCESS_INFO, // info level
            (LPBYTE) &ssStatus,             // address of structure
            sizeof(SERVICE_STATUS_PROCESS), // size of structure
            &dwBytesNeeded ) )              // if buffer too small
        {
            printf("QueryServiceStatusEx failed (%d)\n", (uint32_t)GetLastError());
            break;
        }

        if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
        {
            // Continue to wait and check.

            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
                // No progress made within the wait hint.
                break;
            }
        }
    }

    // Determine whether the service is running.

    if (ssStatus.dwCurrentState == SERVICE_RUNNING)
    {
        printf("Service started successfully.\n");
    }
    else
    {
        printf("Service not started. \n");
        printf("  Current State: %d\n", (uint32_t)ssStatus.dwCurrentState);
        printf("  Exit Code: %d\n", (uint32_t)ssStatus.dwWin32ExitCode);
        printf("  Check Point: %d\n", (uint32_t)ssStatus.dwCheckPoint);
        printf("  Wait Hint: %d\n", (uint32_t)ssStatus.dwWaitHint);
    }

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}

Il faut linker Kernel32.lib et Advapi32.lib, comme indiqué dans la page Microsoft.

Dans Codeblocks, donc, aller dans Project - Build options - Linker settings

Faire "Add" pour ajouter successivement "Kernel32" et "Advapi32" (sans les guillemets).

J'ai testé ce code avec succès avec un service Cygwin sshd installé sur ma machine.

C'est long, car Microsoft aime bien les noms à rallonge, et les préfixe en plus du type, avec des types à la sauce MS, ce qui rend le tout peu lisible, et que le code est conçu pour gérer les erreurs aux différents stades aussi exhaustivement que possible (ce qui est bien).

Ce code est sauvagement extirpé de SvcControl.cpp (c) Microsoft provenant de MSDN dont le lien est plus haut, avec quelques modifications minimes permettant la compilation sous MinGW, et je ne revendique aucun copyright dessus (je ne parle pas pour MS, http://msdn.microsoft.com/en-us/cc300389.aspx .. la Microsoft Limited Public License reproduite en Annexe B de cette page s'appliquerait à ce code qui n'indique pas expressément être soumis à une autre licence :-)


Dal
0
heliconius Messages postés 539 Date d'inscription mardi 1 juillet 2008 Statut Membre Dernière intervention 23 juin 2023 139 > [Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024
3 sept. 2013 à 17:35
Merci infiniment pour cette réponse détaillée. Je suis présentement dans le métro mais dès rentré chez moi ce soir, je détaille, essaye et te donne ici des nouvelles. Bonne fin d'après-midi et A+
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
3 sept. 2013 à 17:42
Ah oui.. tu n'as pas à taper tout cela. Un copier-coller marche aussi bien :-D


Dal
0
heliconius Messages postés 539 Date d'inscription mardi 1 juillet 2008 Statut Membre Dernière intervention 23 juin 2023 139
4 sept. 2013 à 01:01
Bon, comme promis, voilà le résultat.

Ma clef USB est sur Q:

Q:\>tree
Structure du dossier
Le numéro de série du volume est 63E8-781A
Q:.
+---bin
|     exécutables
+---data
¦   +---mysql
¦   +---perso
+---share
    +---charsets
    +---english
    +---french

Q:\>

Copier/Coller du code dans un projet 'my.cbp'.
Linkage avec les librairies Kernel32 et Adapi32.
Compilation:
mingw32-gcc.exe -Wall -g -c "K:\Mes documents\Codeblocks\TestWinDialog\my\main.c" -o obj\Debug\main.o
mingw32-g++.exe -o bin\Debug\my.exe obj\Debug\main.o -lKernel32 -lAdvapi32
Output size is 34.58 KB
Process terminated with status 0 (0 minutes, 1 seconds)
0 errors, 0 warnings (0 minutes, 1 seconds)


Dépôt de l'exécutable en Q:\my.exe
Q:\>cd bin
Q:\bin>..\my
OpenService failed (1060)

Q:\bin>mysqld --install-manual
Service successfully installed.

Q:\bin>..\my
Service start pending...
Service not started.
  Current State: 1
  Exit Code: 1067
  Check Point: 0
  Wait Hint: 0

Q:\bin>

J'ai trouvé une autre solution, pas très catholique ni orthodoxe du tout mais elle répond mieux à mon besoin même si elle est loin d'être parfaite et incomplète :

Nouveau projet dans Code::Block -> mysrv.cbp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    printf("%s","Tapez ^C pour retourner au prompt et lancer le client mysql");
	system("cmd.exe /C \\bin\\mysqld");
	exit(EXIT_SUCCESS);
}


J'ai rien trouvé de mieux pour l'instant.
Le but étant de rajouter MySQL aux programmes PortablesApps de ma clef ; j'aimerais qu'une fois cliqué sur le choix du menu, le serveur soit lancé (pas forcément en tant que service) et qu'une fenêtre soit ouverte, prête pour taper la commande du lancement du client : "mysql -u xxx -p".

Y'a de la galère dans l'air... :o)
Si t'as d'autres infos ou si tu veux que je mette le package à ta disposition, dis-le. En tout cas, merci.
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié par [Dal] le 4/09/2013 à 10:43
Si la solution que tu as trouvée te convient, c'est très bien. A ma connaissance, tu ne peux pas faire autrement pour récupérer la main sur la console sous XP avec les versions actuelles de MySQL, en lançant mysqld ainsi depuis la ligne de commande Windows, ou alors tu ouvres une autre console.

Lancer le service te permettrait de récupérer la main. Si tu veux creuser, lis la suite.

"Service start pending..." signifie que StartService n'a pas retourné d'erreurs. "Current State: 1" pour dwCurrentState signifie "The service has stopped" et "Exit Code: 1067" est un code général Windows qui signifie "The process terminated unexpectedly". Ce sont des erreurs renvoyées par QueryServiceStatusEx, qui est utilisé dans ce code pour vérifier, une fois StartService déclenché, que le service est bien démarré. As-tu regardé les logs Windows dans l'Observateur d'événements (sous Windows XP http://support.microsoft.com/kb/308427/fr) pour en savoir plus ? MySQL a du renvoyer un message d'erreur plus explicite, qui se trouve dans les logs.

Je suppose que tu as des erreurs similaires en faisant "net start mysql" ?

Si c'est le cas, cela signifie que le programme C de lancement du service n'est probablement pas en cause, et qu'il s'agit d'un problème lié à MySQL à son installation ou configuration pour fonctionner en tant que service.

Sinon, d'après la doc MySQL : "Lorsqu'il fonctionne comme un service, mysqld n'a pas accès à la console Windows, et aucune message n'apparaîtra la. Si mysqld ne démarre pas, vérifiez dans le fichier d'erreurs si le serveur a inscrit des messages qui indiquent la cause du problème. Le fichier d'erreurs est situé dans le dossier C:\mysql\data. Il porte le suffixe .err.". As-tu un fichier .err dans le "data" de ton lecteur Q:\ ? As-tu un répertoire c:\mysql\data (et sinon qu'arrive-t-il si tu tentes la même chose après avoir créé un répertoire c:\mysql\data vide) ?

Autrement, la doc MySQL suppose que MySQL est installé dans un répertoire, du genre c:\mysql, les répertoires bin et autres étant des sous-répertoires de ce répertoire. Essaye d'organiser ta clef comme cela, et vois si cela change quelque chose, et si tu as un fichier d'erreurs généré.

D'autre part, lorsque tu installes le service, tu peux spécifier aussi le nom du service et l'emplacement du fichier de configuration. En supposant que tu aies un fichier "my.cnf" à la racine de ta clef (adaptes s'il est ailleurs), et que dans ce fichier de configuration tu aies une section [mysql] :

Q:\mysql\bin\mysqld --install-manual mysql --defaults-file=Q:\my.cnf

Lance cette ligne en tapant le chemin complet Q:\mysql\... au lieu de seulement mysqld (même si tu es sur le bon répertoire).

Vérifie si ton fichier de configuration comporte des informations cohérentes par rapport à ton installation.


Dal
0