main: Add Windows Service support
I've added PIPE_EVENT_SHUTDOWN because calling g_main_loop_quit() do not work when called from another thread. Main thread was sleeping in g_poll() so I needed some way to wake it up. By some strange reason call close(event_pipe[0]) in event_pipe_deinit() hangs. In current implementation that code never reached so that was not a problem :-) I've added a conditional to leave event_pipe[0] open on Win32.
This commit is contained in:
		
				
					committed by
					
						
						Max Kellermann
					
				
			
			
				
	
			
			
			
						parent
						
							9fa3d7c4fa
						
					
				
				
					commit
					e8ebb1af91
				
			@@ -278,6 +278,7 @@ src_mpd_SOURCES = \
 | 
				
			|||||||
	src/log.c \
 | 
						src/log.c \
 | 
				
			||||||
	src/ls.c \
 | 
						src/ls.c \
 | 
				
			||||||
	src/main.c \
 | 
						src/main.c \
 | 
				
			||||||
 | 
						src/main_win32.c \
 | 
				
			||||||
	src/event_pipe.c \
 | 
						src/event_pipe.c \
 | 
				
			||||||
	src/daemon.c \
 | 
						src/daemon.c \
 | 
				
			||||||
	src/AudioCompress/compress.c \
 | 
						src/AudioCompress/compress.c \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,7 +116,10 @@ void event_pipe_deinit(void)
 | 
				
			|||||||
	g_source_remove(event_pipe_source_id);
 | 
						g_source_remove(event_pipe_source_id);
 | 
				
			||||||
	g_io_channel_unref(event_channel);
 | 
						g_io_channel_unref(event_channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef WIN32
 | 
				
			||||||
 | 
						/* By some strange reason this call hangs on Win32 */
 | 
				
			||||||
	close(event_pipe[0]);
 | 
						close(event_pipe[0]);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	close(event_pipe[1]);
 | 
						close(event_pipe[1]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,9 @@ enum pipe_event {
 | 
				
			|||||||
	/** a hardware mixer plugin has detected a change */
 | 
						/** a hardware mixer plugin has detected a change */
 | 
				
			||||||
	PIPE_EVENT_MIXER,
 | 
						PIPE_EVENT_MIXER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** shutdown requested */
 | 
				
			||||||
 | 
						PIPE_EVENT_SHUTDOWN,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PIPE_EVENT_MAX
 | 
						PIPE_EVENT_MAX
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/main.c
									
									
									
									
									
								
							@@ -269,7 +269,25 @@ idle_event_emitted(void)
 | 
				
			|||||||
		client_manager_idle_add(flags);
 | 
							client_manager_idle_add(flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * event_pipe callback function for PIPE_EVENT_SHUTDOWN
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					shutdown_event_emitted(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						g_main_loop_quit(main_loop);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef WIN32
 | 
				
			||||||
 | 
						return win32_main(argc, argv);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						return mpd_main(argc, argv);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int mpd_main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct options options;
 | 
						struct options options;
 | 
				
			||||||
	clock_t start;
 | 
						clock_t start;
 | 
				
			||||||
@@ -324,6 +342,7 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	event_pipe_init();
 | 
						event_pipe_init();
 | 
				
			||||||
	event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
 | 
						event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
 | 
				
			||||||
 | 
						event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	path_global_init();
 | 
						path_global_init();
 | 
				
			||||||
	glue_mapper_init();
 | 
						glue_mapper_init();
 | 
				
			||||||
@@ -392,10 +411,17 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
	   playlist_state_restore() */
 | 
						   playlist_state_restore() */
 | 
				
			||||||
	pc_update_audio();
 | 
						pc_update_audio();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* run the main loop */
 | 
					#ifdef WIN32
 | 
				
			||||||
 | 
						win32_app_started();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* run the main loop */
 | 
				
			||||||
	g_main_loop_run(main_loop);
 | 
						g_main_loop_run(main_loop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WIN32
 | 
				
			||||||
 | 
						win32_app_stopping();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* cleanup */
 | 
						/* cleanup */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_main_loop_unref(main_loop);
 | 
						g_main_loop_unref(main_loop);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								src/main.h
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								src/main.h
									
									
									
									
									
								
							@@ -28,4 +28,45 @@ extern GMainLoop *main_loop;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
extern GCond *main_cond;
 | 
					extern GCond *main_cond;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A entry point for application.
 | 
				
			||||||
 | 
					 * On non-Windows platforms this is called directly from main()
 | 
				
			||||||
 | 
					 * On Windows platform this is called from win32_main()
 | 
				
			||||||
 | 
					 * after doing some initialization.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int mpd_main(int argc, char *argv[]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WIN32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * If program is run as windows service performs nessesary initialization
 | 
				
			||||||
 | 
					 * and then calls mpd_main() with specified arguments.
 | 
				
			||||||
 | 
					 * If program is run as a regular application calls mpd_main() immediately.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					win32_main(int argc, char *argv[]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * When running as a service reports to service control manager
 | 
				
			||||||
 | 
					 * that our service is started.
 | 
				
			||||||
 | 
					 * When running as a console application enables console handler that will
 | 
				
			||||||
 | 
					 * trigger PIPE_EVENT_SHUTDOWN when user closes console window
 | 
				
			||||||
 | 
					 * or presses Ctrl+C.
 | 
				
			||||||
 | 
					 * This function should be called just before entering main loop.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					win32_app_started(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * When running as a service reports to service control manager
 | 
				
			||||||
 | 
					 * that our service is about to stop.
 | 
				
			||||||
 | 
					 * When running as a console application enables console handler that will
 | 
				
			||||||
 | 
					 * catch all shutdown requests and ignore them.
 | 
				
			||||||
 | 
					 * This function should be called just after leaving main loop.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					win32_app_stopping(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										154
									
								
								src/main_win32.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/main_win32.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2010 The Music Player Daemon Project
 | 
				
			||||||
 | 
					 * http://www.musicpd.org
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.,
 | 
				
			||||||
 | 
					 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "main.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef WIN32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "event_pipe.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <glib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define WINVER 0x0501
 | 
				
			||||||
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int service_argc;
 | 
				
			||||||
 | 
					static char **service_argv;
 | 
				
			||||||
 | 
					static char service_name[] = "";
 | 
				
			||||||
 | 
					static BOOL ignore_console_events;
 | 
				
			||||||
 | 
					static SERVICE_STATUS_HANDLE service_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void WINAPI
 | 
				
			||||||
 | 
					service_main(DWORD argc, CHAR *argv[]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static SERVICE_TABLE_ENTRY service_registry[] = {
 | 
				
			||||||
 | 
						{service_name, service_main},
 | 
				
			||||||
 | 
						{NULL, NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					service_notify_status(DWORD status_code)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SERVICE_STATUS current_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						current_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
 | 
				
			||||||
 | 
						current_status.dwControlsAccepted = status_code == SERVICE_START_PENDING
 | 
				
			||||||
 | 
							? 0
 | 
				
			||||||
 | 
							: SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						current_status.dwCurrentState = status_code;
 | 
				
			||||||
 | 
						current_status.dwWin32ExitCode = NO_ERROR;
 | 
				
			||||||
 | 
						current_status.dwCheckPoint = 0;
 | 
				
			||||||
 | 
						current_status.dwWaitHint = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SetServiceStatus(service_handle, ¤t_status);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DWORD WINAPI
 | 
				
			||||||
 | 
					service_dispatcher(G_GNUC_UNUSED DWORD control, G_GNUC_UNUSED DWORD event_type,
 | 
				
			||||||
 | 
							   G_GNUC_UNUSED void *event_data, G_GNUC_UNUSED void *context)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (control) {
 | 
				
			||||||
 | 
						case SERVICE_CONTROL_SHUTDOWN:
 | 
				
			||||||
 | 
						case SERVICE_CONTROL_STOP:
 | 
				
			||||||
 | 
							event_pipe_emit(PIPE_EVENT_SHUTDOWN);
 | 
				
			||||||
 | 
							return NO_ERROR;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return NO_ERROR;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void WINAPI
 | 
				
			||||||
 | 
					service_main(G_GNUC_UNUSED DWORD argc, G_GNUC_UNUSED CHAR *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DWORD error_code;
 | 
				
			||||||
 | 
						gchar* error_message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						service_handle =
 | 
				
			||||||
 | 
							RegisterServiceCtrlHandlerEx(service_name,
 | 
				
			||||||
 | 
										     service_dispatcher, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (service_handle == 0) {
 | 
				
			||||||
 | 
							error_code = GetLastError();
 | 
				
			||||||
 | 
							error_message = g_win32_error_message(error_code);
 | 
				
			||||||
 | 
							g_error("RegisterServiceCtrlHandlerEx() failed: %s",
 | 
				
			||||||
 | 
								error_message);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						service_notify_status(SERVICE_START_PENDING);
 | 
				
			||||||
 | 
						mpd_main(service_argc, service_argv);
 | 
				
			||||||
 | 
						service_notify_status(SERVICE_STOPPED);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BOOL WINAPI
 | 
				
			||||||
 | 
					console_handler(DWORD event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (event) {
 | 
				
			||||||
 | 
						case CTRL_C_EVENT:
 | 
				
			||||||
 | 
						case CTRL_CLOSE_EVENT:
 | 
				
			||||||
 | 
							if (!ignore_console_events)
 | 
				
			||||||
 | 
								event_pipe_emit(PIPE_EVENT_SHUTDOWN);
 | 
				
			||||||
 | 
							return TRUE;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return FALSE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int win32_main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DWORD error_code;
 | 
				
			||||||
 | 
						gchar* error_message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						service_argc = argc;
 | 
				
			||||||
 | 
						service_argv = argv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (StartServiceCtrlDispatcher(service_registry))
 | 
				
			||||||
 | 
							return 0; /* run as service successefully */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error_code = GetLastError();
 | 
				
			||||||
 | 
						if (error_code == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
 | 
				
			||||||
 | 
							/* running as console app */
 | 
				
			||||||
 | 
							SetConsoleTitle("Music Player Daemon");
 | 
				
			||||||
 | 
							ignore_console_events = TRUE;
 | 
				
			||||||
 | 
							SetConsoleCtrlHandler(console_handler, TRUE);
 | 
				
			||||||
 | 
							return mpd_main(argc, argv);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error_message = g_win32_error_message(error_code);
 | 
				
			||||||
 | 
						g_error("StartServiceCtrlDispatcher() failed: %s", error_message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void win32_app_started()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (service_handle != 0)
 | 
				
			||||||
 | 
							service_notify_status(SERVICE_RUNNING);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ignore_console_events = FALSE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void win32_app_stopping()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (service_handle != 0)
 | 
				
			||||||
 | 
							service_notify_status(SERVICE_STOP_PENDING);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ignore_console_events = TRUE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		Reference in New Issue
	
	Block a user