void redemption_new_session(CryptoContext & cctx, char const * config_filename) { char text[256]; char source_ip[256]; int target_port = 0; char real_target_ip[256]; union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; int sock_len = sizeof(u); Inifile ini; { ConfigurationLoader cfg_loader(ini.configuration_holder(), config_filename); } init_signals(); snprintf(text, 255, "redemption_%8.8x_main_term", unsigned(getpid())); getpeername(0, &u.s, reinterpret_cast<socklen_t *>(&sock_len)); strcpy(source_ip, inet_ntoa(u.s4.sin_addr)); union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } localAddress; socklen_t addressLength = sizeof(localAddress); int sck = 0; if (-1 == getsockname(sck, &localAddress.s, &addressLength)){ LOG(LOG_INFO, "getsockname failed error=%s", strerror(errno)); _exit(1); } target_port = localAddress.s4.sin_port; strcpy(real_target_ip, inet_ntoa(localAddress.s4.sin_addr)); if (ini.get<cfg::globals::enable_ip_transparent>()) { const int source_port = 0; char target_ip[256]; strcpy(target_ip, inet_ntoa(localAddress.s4.sin_addr)); int fd = open("/proc/net/ip_conntrack", O_RDONLY); // source and dest are inverted because we get the information we want from reply path rule int res = parse_ip_conntrack(fd, target_ip, source_ip, target_port, source_port, real_target_ip, sizeof(real_target_ip)); if (res){ LOG(LOG_WARNING, "Failed to get transparent proxy target from ip_conntrack"); } close(fd); } ini.set_acl<cfg::globals::host>(source_ip); ini.set_acl<cfg::globals::target>(real_target_ip); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Setting new session socket to %d\n", sck); } int nodelay = 1; if (0 == setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&nodelay), sizeof(nodelay))){ Session session(sck, ini, cctx); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Session::end of Session(%u)", sck); } shutdown(sck, 2); close(sck); } else { LOG(LOG_INFO, "Failed to set socket TCP_NODELAY option on client socket"); } }
virtual Server_status start(int incoming_sck) { union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; unsigned int sin_size = sizeof(u); memset(&u, 0, sin_size); int sck = accept(incoming_sck, &u.s, &sin_size); if (-1 == sck) { LOG(LOG_INFO, "Accept failed on socket %u (%s)", incoming_sck, strerror(errno)); _exit(1); } char source_ip[256]; strcpy(source_ip, inet_ntoa(u.s4.sin_addr)); const int source_port = ntohs(u.s4.sin_port); /* start new process */ const pid_t pid = fork(); switch (pid) { case 0: /* child */ { close(incoming_sck); Inifile ini; ini.set<cfg::debug::config>(this->debug_config); { ConfigurationLoader cfg_loader(ini, this->config_filename.c_str()); } if (ini.get<cfg::globals::wab_agent_alternate_shell>().empty()) { ini.set<cfg::globals::wab_agent_alternate_shell>( this->parametersHldr.get_agent_alternate_shell() ); } ini.get_ref<cfg::crypto::key0>().setmem(this->parametersHldr.get_crypto_key_0()); ini.get_ref<cfg::crypto::key1>().setmem(this->parametersHldr.get_crypto_key_1()); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Setting new session socket to %d\n", sck); } union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } localAddress; socklen_t addressLength = sizeof(localAddress); if (-1 == getsockname(sck, &localAddress.s, &addressLength)){ LOG(LOG_INFO, "getsockname failed error=%s", strerror(errno)); _exit(1); } char target_ip[256]; const int target_port = ntohs(localAddress.s4.sin_port); // strcpy(real_target_ip, inet_ntoa(localAddress.s4.sin_addr)); strcpy(target_ip, inet_ntoa(localAddress.s4.sin_addr)); if (0 != strcmp(source_ip, "127.0.0.1")){ // do not log early messages for localhost (to avoid tracing in watchdog) LOG(LOG_INFO, "src=%s sport=%d dst=%s dport=%d", source_ip, source_port, target_ip, target_port); } char real_target_ip[256]; if (ini.get<cfg::globals::enable_ip_transparent>() && (0 != strcmp(source_ip, "127.0.0.1"))) { int fd = open("/proc/net/ip_conntrack", O_RDONLY); // source and dest are inverted because we get the information we want from reply path rule int res = parse_ip_conntrack(fd, target_ip, source_ip, target_port, source_port, real_target_ip, sizeof(real_target_ip), 1); if (res){ LOG(LOG_WARNING, "Failed to get transparent proxy target from ip_conntrack: %d", fd); } close(fd); if (setgid(this->gid) != 0){ LOG(LOG_WARNING, "Changing process group to %u failed with error: %s\n", this->gid, strerror(errno)); _exit(1); } if (setuid(this->uid) != 0){ LOG(LOG_WARNING, "Changing process group to %u failed with error: %s\n", this->gid, strerror(errno)); _exit(1); } LOG(LOG_INFO, "src=%s sport=%d dst=%s dport=%d", source_ip, source_port, real_target_ip, target_port); } else { ::memset(real_target_ip, 0, sizeof(real_target_ip)); } int nodelay = 1; if (0 == setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay))){ // Create session file int child_pid = getpid(); char session_file[256]; sprintf(session_file, "%s/redemption/session_%d.pid", PID_PATH, child_pid); int fd = open(session_file, O_WRONLY | O_CREAT, S_IRWXU); if (fd == -1) { LOG(LOG_ERR, "Writing process id to SESSION ID FILE failed. Maybe no rights ?:%d:%d\n", errno, strerror(errno)); _exit(1); } char text[256]; const size_t lg = snprintf(text, 255, "%d", child_pid); if (write(fd, text, lg) == -1) { LOG(LOG_ERR, "Couldn't write pid to %s: %s", PID_PATH "/redemption/session_<pid>.pid", strerror(errno)); _exit(1); } close(fd); // Launch session if (0 != strcmp(source_ip, "127.0.0.1")){ // do not log early messages for localhost (to avoid tracing in watchdog) LOG(LOG_INFO, "New session on %u (pid=%u) from %s to %s", (unsigned)sck, (unsigned)child_pid, source_ip, (real_target_ip[0] ? real_target_ip : target_ip)); } ini.set_acl<cfg::globals::host>(source_ip); // ini.context_set_value(AUTHID_TARGET, real_target_ip); ini.set_acl<cfg::globals::target>(target_ip); if (ini.get<cfg::globals::enable_ip_transparent>() && strncmp(target_ip, real_target_ip, strlen(real_target_ip))) { ini.set_acl<cfg::context::real_target_device>(real_target_ip); } Session session(sck, ini); // Suppress session file unlink(session_file); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Session::end of Session(%u)", sck); } shutdown(sck, 2); close(sck); } else { LOG(LOG_ERR, "Failed to set socket TCP_NODELAY option on client socket"); } return START_WANT_STOP; } break; default: /* father */ { close(sck); } break; case -1: // error forking LOG(LOG_ERR, "Error creating process for new session : %s\n", strerror(errno)); break; } return START_FAILED; }
int main(int argc, char * argv[]) { openlog("transparent", LOG_CONS | LOG_PERROR, LOG_USER); const char * copyright_notice = "\n" "ReDemPtion Transparent Proxy " VERSION ".\n" "Copyright (C) Wallix 2010-2015.\n" "Christophe Grosjean, Raphael Zhou.\n" "\n" ; std::string input_filename; std::string output_filename; std::string target_device; uint32_t target_port; std::string username; std::string password; std::string record_filename; std::string play_filename; std::string persistent_key_list_filename; persistent_key_list_filename = "./PersistentKeyList.bin"; target_port = 3389; program_options::options_description desc({ {'h', "help", "produce help message"}, {'v', "version", "show software version"}, {'i', "input-file", &input_filename, "input ini file name"}, {'o', "output-file", &output_filename, "output int file name"}, {'t', "target-device", &target_device, "target device[:port]"}, {'u', "username", &username, "username"}, {'p', "password", &password, "password"}, {'k', "key-list-file", &persistent_key_list_filename, "persistent key list file name"}, {'r', "record-file", &record_filename, "record file name"}, {'d', "play-file", &play_filename, "play file name"}, }); auto options = program_options::parse_command_line(argc, argv, desc); if (options.count("help") > 0) { std::cout << copyright_notice; std::cout << "Usage: rdptproxy [options]\n\n"; std::cout << desc << endl; exit(0); } if (options.count("version") > 0) { std::cout << copyright_notice; exit(0); } if ( target_device.empty() && play_filename.empty()) { std::cerr << "Missing target device or play file name: use -t target or -d filename\n\n"; exit(-1); } if ( !target_device.empty() && !play_filename.empty()) { std::cerr << "Use -t target or -d filename\n\n"; exit(-1); } if ( !output_filename.empty() && !play_filename.empty()) { std::cerr << "Use -o filename or -d filename\n\n"; exit(-1); } if ( !record_filename.empty() && !play_filename.empty()) { std::cerr << "Use -r filename or -d filename\n\n"; exit(-1); } if ( !input_filename.empty() && !output_filename.empty()) { std::cerr << "Use -i filename or -o filename\n\n"; exit(-1); } if (!target_device.empty()) { size_t pos = target_device.find(':'); if (pos != string::npos) { target_port = atoi(target_device.substr(pos + 1).c_str()); target_device.resize(pos); } if (username.c_str()[0] == 0) { std::cerr << "Missing username : use -u username\n\n"; exit(-1); } } if (password.empty()) { password = ""; } // This server only support one incoming connection before closing listener class ServerOnce : public Server { public: int sck; char ip_source[256]; ServerOnce() : sck(0) { this->ip_source[0] = 0; } virtual Server_status start(int incoming_sck) { union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; unsigned int sin_size = sizeof(u); memset(&u, 0, sin_size); this->sck = accept(incoming_sck, &u.s, &sin_size); strcpy(this->ip_source, inet_ntoa(u.s4.sin_addr)); LOG(LOG_INFO, "Incoming socket to %d (ip=%s)\n", this->sck, this->ip_source); return START_WANT_STOP; } } one_shot_server; Listen listener(one_shot_server, 0, 3389, true, 5); // 25 seconds to connect, or timeout listener.run(); Inifile ini; ConfigurationLoader cfg_loader(ini, CFG_PATH "/" RDPPROXY_INI); int nodelay = 1; if (-1 == setsockopt( one_shot_server.sck, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay , sizeof(nodelay))) { LOG(LOG_ERR, "Failed to set socket TCP_NODELAY option on client socket"); } SocketTransport front_trans( "RDP Client", one_shot_server.sck, "0.0.0.0", 0 , ini.debug.front, 0); wait_obj front_event; LCGRandom gen(0); // Remove existing Persistent Key List file. unlink(persistent_key_list_filename.c_str()); OutFileTransport * persistent_key_list_oft = NULL; int persistent_key_list_ofd; persistent_key_list_ofd = open(persistent_key_list_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (persistent_key_list_ofd != -1) { persistent_key_list_oft = new OutFileTransport(persistent_key_list_ofd); } else { LOG(LOG_ERR, "Failed to open Persistent Key List file to writing: name=\"%s\"", persistent_key_list_filename.c_str()); } const bool fastpath_support = true; const bool mem3blt_support = true; Front front(front_trans, SHARE_PATH "/" DEFAULT_FONT_NAME, gen, ini, fastpath_support, mem3blt_support, input_filename.c_str(), persistent_key_list_oft); null_mod no_mod(front); while (front.up_and_running == 0) { front.incoming(no_mod); } LOG(LOG_INFO, "hostname=\"%s\"", front.client_info.hostname); try { if (target_device.empty()) { TransparentReplayMod mod(front, play_filename.c_str(), front.client_info.width, front.client_info.height, NULL, ini.font); run_mod(mod, front, front_event, nullptr, &front_trans); } else { OutFileTransport * record_oft = NULL; int record_fd = -1; if (!record_filename.empty()) { record_fd = open(record_filename.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (record_fd != -1) { record_oft = new OutFileTransport(record_fd); } else { LOG(LOG_ERR, "Failed to open record file to writing: name=\"%s\"", record_filename.c_str()); } } InFileTransport * persistent_key_list_ift = NULL; int persistent_key_list_ifd; persistent_key_list_ifd = open(persistent_key_list_filename.c_str(), O_RDONLY); if (persistent_key_list_ifd != -1) { persistent_key_list_ift = new InFileTransport(persistent_key_list_ifd); } else { LOG(LOG_ERR, "Failed to open Persistent Key List file to reading: name=\"%s\"", persistent_key_list_filename.c_str()); } int client_sck = ip_connect(target_device.c_str(), target_port, 3, 1000, ini.debug.mod_rdp); SocketTransport mod_trans( "RDP Server", client_sck, target_device.c_str(), target_port , ini.debug.mod_rdp, &ini.context.auth_error_message); ClientInfo client_info = front.client_info; ModRDPParams mod_rdp_params( username.c_str() , password.c_str() , target_device.c_str() , "0.0.0.0" // client ip is silenced , front.keymap.key_flags , ini.debug.mod_rdp ); //mod_rdp_params.enable_tls = true; mod_rdp_params.enable_nla = ini.mod_rdp.enable_nla; mod_rdp_params.enable_krb = ini.mod_rdp.enable_kerberos; //mod_rdp_params.enable_fastpath = true; //mod_rdp_params.enable_mem3blt = true; mod_rdp_params.enable_bitmap_update = ini.globals.enable_bitmap_update; //mod_rdp_params.enable_new_pointer = true; mod_rdp_params.enable_transparent_mode = true; mod_rdp_params.output_filename = (output_filename.empty() ? "" : output_filename.c_str()); mod_rdp_params.persistent_key_list_transport = persistent_key_list_ift; mod_rdp_params.transparent_recorder_transport = record_oft; mod_rdp_params.auth_channel = ini.globals.auth_channel; mod_rdp_params.alternate_shell = ini.globals.alternate_shell.get_cstr(); mod_rdp_params.shell_working_directory = ini.globals.shell_working_directory.get_cstr(); mod_rdp_params.rdp_compression = ini.mod_rdp.rdp_compression; mod_rdp_params.disconnect_on_logon_user_change = ini.mod_rdp.disconnect_on_logon_user_change; mod_rdp_params.open_session_timeout = ini.mod_rdp.open_session_timeout; mod_rdp_params.certificate_change_action = ini.mod_rdp.certificate_change_action; mod_rdp_params.extra_orders = ini.mod_rdp.extra_orders.c_str(); mod_rdp_params.enable_persistent_disk_bitmap_cache = ini.mod_rdp.persistent_disk_bitmap_cache; mod_rdp_params.enable_cache_waiting_list = ini.mod_rdp.cache_waiting_list; mod_rdp_params.password_printing_mode = ini.debug.password; mod_rdp_params.cache_verbose = ini.debug.cache; mod_rdp_params.allow_channels = &(ini.mod_rdp.allow_channels); mod_rdp_params.deny_channels = &(ini.mod_rdp.deny_channels); mod_rdp mod(mod_trans, front, client_info, ini.mod_rdp.redir_info, gen, mod_rdp_params); run_mod(mod, front, front_event, &mod_trans, &front_trans); if (client_sck != -1) { shutdown(client_sck, 2); close(client_sck); } if (persistent_key_list_ifd != -1) { delete persistent_key_list_ift; close(persistent_key_list_ifd); } if (record_fd != -1) { delete record_oft; close(record_fd); } } } // try catch (Error & e) { LOG(LOG_ERR, "errid = %d", e.id); } front.disconnect(); if (persistent_key_list_ofd != -1) { delete persistent_key_list_oft; close(persistent_key_list_ofd); } shutdown(one_shot_server.sck, 2); close(one_shot_server.sck); LOG(LOG_INFO, "Listener closed\n"); LOG(LOG_INFO, "Incoming socket %d (ip=%s)\n", one_shot_server.sck, one_shot_server.ip_source); return 0; }
void redemption_new_session() { char text[256]; char source_ip[256]; int source_port = 0; char target_ip[256]; int target_port = 0; char real_target_ip[256]; union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; int sock_len = sizeof(u); Inifile ini; ConfigurationLoader cfg_loader(ini, CFG_PATH "/" RDPPROXY_INI); init_signals(); snprintf(text, 255, "redemption_%8.8x_main_term", getpid()); getpeername(0, &u.s, (socklen_t *)&sock_len); strcpy(source_ip, inet_ntoa(u.s4.sin_addr)); union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } localAddress; socklen_t addressLength = sizeof(localAddress); int sck = 0; if (-1 == getsockname(sck, &localAddress.s, &addressLength)){ LOG(LOG_INFO, "getsockname failed error=%s", strerror(errno)); _exit(1); } target_port = localAddress.s4.sin_port; strcpy(real_target_ip, inet_ntoa(localAddress.s4.sin_addr)); if (ini.globals.enable_ip_transparent) { strcpy(target_ip, inet_ntoa(localAddress.s4.sin_addr)); int fd = open("/proc/net/ip_conntrack", O_RDONLY); // source and dest are inverted because we get the information we want from reply path rule int res = parse_ip_conntrack(fd, target_ip, source_ip, target_port, source_port, real_target_ip, sizeof(real_target_ip)); if (res){ LOG(LOG_WARNING, "Failed to get transparent proxy target from ip_conntrack"); } close(fd); } ini.context_set_value(AUTHID_HOST, source_ip); ini.context_set_value(AUTHID_TARGET, real_target_ip); if (ini.debug.session){ LOG(LOG_INFO, "Setting new session socket to %d\n", sck); } int nodelay = 1; if (0 == setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay))){ wait_obj front_event(sck); Session session(front_event, sck, &ini); if (ini.debug.session){ LOG(LOG_INFO, "Session::end of Session(%u)", sck); } shutdown(sck, 2); close(sck); } else { LOG(LOG_INFO, "Failed to set socket TCP_NODELAY option on client socket"); } }
int app_proxy( int argc, char const * const * argv, const char * copyright_notice , CryptoContext & cctx , ExtraOptions const & extrax_options, ExtracOptionChecker extrac_options_checker , PreLoopFn pre_loop_fn = PreLoopFn() ) { setlocale(LC_CTYPE, "C"); const unsigned uid = getuid(); const unsigned gid = getgid(); unsigned euid = uid; unsigned egid = gid; std::string config_filename = CFG_PATH "/" RDPPROXY_INI; program_options::options_description desc({ {'h', "help", "produce help message"}, {'v', "version", "show software version"}, {'k', "kill", "shut down rdpproxy"}, {'n', "nodaemon", "don't fork into background"}, {'u', "uid", &euid, "run with given uid"}, {'g', "gid", &egid, "run with given gid"}, //{'t', "trace", "trace behaviour"}, {'c', "check", "check installation files"}, {'f', "force", "remove application lock file"}, {'i', "inetd", "launch redemption with inetd like launcher"}, {"config-file", &config_filename, "used an another ini file"}, //{"test", "check Inifile syntax"} }); for (extra_option const & extra : extrax_options) { desc.add({extra.option_long, extra.description}); } auto options = program_options::parse_command_line(argc, const_cast<char**>(argv), desc); if (options.count("kill")) { int status = shutdown(PID_PATH "/redemption/" LOCKFILE); if (status){ // TODO check the real error that occured std::clog << "problem opening " << PID_PATH "/redemption/" LOCKFILE << "." " Maybe rdpproxy is not running\n"; } return status; } if (options.count("help")) { std::cout << copyright_notice << "\n\n"; std::cout << "Usage: rdpproxy [options]\n\n"; std::cout << desc << std::endl; return 0; } if (options.count("version")) { std::cout << copyright_notice << std::endl; return 0; } { bool quit = false; if (int status = extrac_options_checker(options, &quit)) { return status; } else if (quit) { return 0; } } openlog("rdpproxy", LOG_CONS | LOG_PERROR, LOG_USER); if (options.count("check")) { bool user_check_file_result = ((uid != euid) || (gid != egid)) ? CheckFile::check(user_check_file_list) : true; /* setgid(egid); setuid(euid); */ bool euser_check_file_result = CheckFile::check(euser_check_file_list); /* setgid(gid); setuid(uid); */ if ((uid != euid) || (gid != egid)) { CheckFile::ShowAll(user_check_file_list, uid, gid); } CheckFile::ShowAll(euser_check_file_list, euid, egid); if (!user_check_file_result || !euser_check_file_result) { if ((uid != euid) || (gid != egid)) { CheckFile::ShowErrors(user_check_file_list, uid, gid); } CheckFile::ShowErrors(euser_check_file_list, euid, egid); LOG(LOG_INFO, "%s", "Please verify that all tests passed. If not, " "you may need to remove " PID_PATH "/redemption/" LOCKFILE " or reinstall rdpproxy if some configuration " "files are missing."); } return 0; } if (options.count("inetd")) { redemption_new_session(cctx, config_filename.c_str()); return 0; } // if -f (force option) is set // force kill running rdpproxy // force remove pid file // don't check if it fails (proxy may be allready stopped) // and try to continue normal start process afterward if (mkdir(PID_PATH "/redemption", 0700) < 0){ // TODO check only for relevant errors (exists with expected permissions is OK) } if (chown(PID_PATH "/redemption", euid, egid) < 0){ LOG(LOG_INFO, "Failed to set owner %u.%u to " PID_PATH "/redemption", euid, egid); return 1; } if (options.count("force")){ shutdown(PID_PATH "/redemption/" LOCKFILE); } if (0 == access(PID_PATH "/redemption/" LOCKFILE, F_OK)) { std::clog << "File " << PID_PATH "/redemption/" LOCKFILE << " already exists. " "It looks like rdpproxy is already running, " "if not, try again with -f (force) option or delete the " PID_PATH "/redemption/" LOCKFILE " file and try again\n"; return 1; } /* write the pid to file */ int fd = open(PID_PATH "/redemption/" LOCKFILE, O_WRONLY | O_CREAT, S_IRWXU); if (fd == -1) { std::clog << "Writing process id to " PID_PATH "/redemption/" LOCKFILE " failed. Maybe no rights ?" << " : " << errno << ":'" << strerror(errno) << "'\n"; return 1; } { io::posix::fdbuf file(fd); const int pid = getpid(); char text[256]; size_t lg = snprintf(text, 255, "%d", pid); if (file.write(text, lg) == -1) { LOG(LOG_ERR, "Couldn't write pid to %s: %s", PID_PATH "/redemption/" LOCKFILE, strerror(errno)); return 1; } } if (!options.count("nodaemon")) { daemonize(PID_PATH "/redemption/" LOCKFILE); } Inifile ini; { ConfigurationLoader cfg_loader(ini.configuration_holder(), config_filename.c_str()); } OpenSSL_add_all_digests(); if (!ini.get<cfg::globals::enable_ip_transparent>()) { if (setgid(egid) != 0){ LOG(LOG_ERR, "Changing process group to %u failed with error: %s\n", egid, strerror(errno)); return 1; } if (setuid(euid) != 0){ LOG(LOG_ERR, "Changing process user to %u failed with error: %s\n", euid, strerror(errno)); return 1; } } pre_loop_fn(ini); LOG(LOG_INFO, "ReDemPtion " VERSION " starting"); redemption_main_loop(ini, cctx, euid, egid, std::move(config_filename)); /* delete the .pid file if it exists */ /* don't care about errors. */ /* If we are not in daemon mode this file will not exists, */ /* hence some errors are expected */ unlink(PID_PATH "/redemption/" LOCKFILE); return 0; }