diff --git a/README.md b/README.md index 305388253..4f5bdc43b 100755 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ Compare SRS with other media server. | RTMP Edge | Stable | X | X | Stable | X | | RTMP Backup | Stable | X | X | X | X | | VHOST | Stable | X | X | Stable | Stable | -| Reload | Stable | Stable | X | X | X | +| Reload | Stable | X | X | X | X | | Forward | Stable | X | X | X | X | | ATC | Stable | X | X | X | X | @@ -312,6 +312,7 @@ Remark: 1. HLS aonly: The HLS audio only streaming delivery. 1. BW check: The bandwidth check. 1. Security: To allow or deny stream publish or play. +1. Reload: Nginx supports reload, but not nginx-rtmp. ## Releases @@ -346,6 +347,8 @@ Remark: * v3.0, 2015-03-15, fork srs2 and start srs3. 3.0.0 ### SRS 2.0 history + +* v2.0, 2015-05-30, fix [#209](https://github.com/simple-rtmp-server/srs/issues/209) cleanup hls when stop and timeout. 2.0.173. * v2.0, 2015-05-29, fix [#409](https://github.com/simple-rtmp-server/srs/issues/409) support pure video hls. 2.0.172. * v2.0, 2015-05-28, support [srs-dolphin][srs-dolphin], the multiple-process SRS. * v2.0, 2015-05-24, fix [#404](https://github.com/simple-rtmp-server/srs/issues/404) register handler then start http thread. 2.0.167. diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 211a2d1d8..cab0cd054 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -618,9 +618,14 @@ vhost with-hls.srs.com { # h264, vn # default: h264 hls_vcodec h264; - # whether cleanup the old ts files. + # whether cleanup the old expired ts files. # default: on hls_cleanup on; + # the timeout in seconds to dispose the hls, + # dispose is to remove all hls files, m3u8 and ts files. + # when timeout or server terminate, dispose hls. + # default: 300 + hls_dispose 300; # the max size to notify hls, # to read max bytes from ts of specified cdn network, # @remark only used when on_hls_notify is config. diff --git a/trunk/etc/init.d/srs b/trunk/etc/init.d/srs index 9cfb7abda..3efcda350 100755 --- a/trunk/etc/init.d/srs +++ b/trunk/etc/init.d/srs @@ -109,12 +109,12 @@ stop() { ok_msg "Stopping SRS(pid ${srs_pid})..." # process exists, try to kill to stop normally - for((i=0;i<30;i++)); do + for((i=0;i<100;i++)); do load_process_info if [[ 0 -eq $? ]]; then kill -s SIGTERM ${srs_pid} 2>/dev/null ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "send signal SIGTERM failed ret=$ret"; return $ret; fi - sleep 0.1 + sleep 0.3 else ok_msg "SRS stopped by SIGTERM" # delete the pid file when stop success. diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index cfd57f57b..056f1efdd 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -1564,7 +1564,8 @@ int SrsConfig::check_config() string m = conf->at(j)->name.c_str(); if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" - && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe" + && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" + && m != "hls_wait_keyframe" && m != "hls_dispose" ) { ret = ERROR_SYSTEM_CONFIG_INVALID; srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); @@ -3561,6 +3562,24 @@ bool SrsConfig::get_hls_cleanup(string vhost) return SRS_CONF_PERFER_TRUE(conf->arg0()); } +int SrsConfig::get_hls_dispose(string vhost) +{ + SrsConfDirective* conf = get_hls(vhost); + + int DEFAULT = 300; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_dispose"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + bool SrsConfig::get_hls_wait_keyframe(string vhost) { SrsConfDirective* hls = get_hls(vhost); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 253489683..eb0374540 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -984,6 +984,10 @@ public: * whether cleanup the old ts files. */ virtual bool get_hls_cleanup(std::string vhost); + /** + * the timeout to dispose the hls. + */ + virtual int get_hls_dispose(std::string vhost); /** * whether reap the ts when got keyframe. */ diff --git a/trunk/src/app/srs_app_ffmpeg.cpp b/trunk/src/app/srs_app_ffmpeg.cpp index 373a1a004..53b24f06f 100644 --- a/trunk/src/app/srs_app_ffmpeg.cpp +++ b/trunk/src/app/srs_app_ffmpeg.cpp @@ -35,6 +35,7 @@ using namespace std; #include #include #include +#include #ifdef SRS_AUTO_FFMPEG_STUB @@ -51,6 +52,7 @@ using namespace std; SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin) { started = false; + fast_stopped = false; pid = -1; ffmpeg = ffmpeg_bin; @@ -402,6 +404,10 @@ int SrsFFMPEG::start() // child process: ffmpeg encoder engine. if (pid == 0) { + // ignore the SIGINT and SIGTERM + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + // redirect logs to file. int log_fd = -1; int flags = O_CREAT|O_WRONLY|O_APPEND; @@ -479,6 +485,11 @@ int SrsFFMPEG::cycle() return ret; } + // ffmpeg is prepare to stop, donot cycle. + if (fast_stopped) { + return ret; + } + int status = 0; pid_t p = waitpid(pid, &status, WNOHANG); @@ -509,24 +520,35 @@ void SrsFFMPEG::stop() // when rewind, upstream will stop publish(unpublish), // unpublish event will stop all ffmpeg encoders, // then publish will start all ffmpeg encoders. - if (pid > 0) { - if (kill(pid, SIGKILL) < 0) { - srs_warn("kill the encoder failed, ignored. pid=%d", pid); - } - - // wait for the ffmpeg to quit. - // ffmpeg will gracefully quit if signal is: - // 1) SIGHUP 2) SIGINT 3) SIGQUIT - // other signals, directly exit(123), for example: - // 9) SIGKILL 15) SIGTERM - int status = 0; - if (waitpid(pid, &status, 0) < 0) { - srs_warn("wait the encoder quit failed, ignored. pid=%d", pid); - } - - srs_trace("stop the encoder success. pid=%d", pid); - pid = -1; + int ret = srs_kill_forced(pid); + if (ret != ERROR_SUCCESS) { + srs_warn("ignore kill the encoder failed, pid=%d. ret=%d", pid, ret); + return; } + + // terminated, set started to false to stop the cycle. + started = false; +} + +void SrsFFMPEG::fast_stop() +{ + int ret = ERROR_SUCCESS; + + if (!started) { + return; + } + + if (pid <= 0) { + return; + } + + if (kill(pid, SIGTERM) < 0) { + ret = ERROR_SYSTEM_KILL; + srs_warn("ignore fast stop ffmpeg failed, pid=%d. ret=%d", pid, ret); + return; + } + + return; } #endif diff --git a/trunk/src/app/srs_app_ffmpeg.hpp b/trunk/src/app/srs_app_ffmpeg.hpp index 7c075c477..f96af8638 100644 --- a/trunk/src/app/srs_app_ffmpeg.hpp +++ b/trunk/src/app/srs_app_ffmpeg.hpp @@ -45,6 +45,8 @@ class SrsFFMPEG { private: bool started; + // whether SIGINT send but need to wait or SIGKILL. + bool fast_stopped; pid_t pid; private: std::string log_file; @@ -83,7 +85,25 @@ public: virtual int initialize_copy(); virtual int start(); virtual int cycle(); + /** + * send SIGTERM then SIGKILL to ensure the process stopped. + * the stop will wait [0, SRS_PROCESS_QUIT_TIMEOUT_MS] depends on the + * process quit timeout. + * @remark use fast_stop before stop one by one, when got lots of process to quit. + */ virtual void stop(); +public: + /** + * the fast stop is to send a SIGTERM. + * for example, the ingesters owner lots of FFMPEG, it will take a long time + * to stop one by one, instead the ingesters can fast_stop all FFMPEG, then + * wait one by one to stop, it's more faster. + * @remark user must use stop() to ensure the ffmpeg to stopped. + * @remark we got N processes to stop, compare the time we spend, + * when use stop without fast_stop, we spend maybe [0, SRS_PROCESS_QUIT_TIMEOUT_MS * N] + * but use fast_stop then stop, the time is almost [0, SRS_PROCESS_QUIT_TIMEOUT_MS]. + */ + virtual void fast_stop(); }; #endif diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index a11711b1a..6ac0c2b5f 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -305,6 +305,37 @@ SrsHlsMuxer::~SrsHlsMuxer() srs_freep(context); } +void SrsHlsMuxer::dispose() +{ + if (should_write_file) { + std::vector::iterator it; + for (it = segments.begin(); it != segments.end(); ++it) { + SrsHlsSegment* segment = *it; + if (unlink(segment->full_path.c_str()) < 0) { + srs_warn("dispose unlink path failed, file=%s.", segment->full_path.c_str()); + } + srs_freep(segment); + } + segments.clear(); + + if (current) { + std::string path = current->full_path + ".tmp"; + if (unlink(path.c_str()) < 0) { + srs_warn("dispose unlink path failed, file=%s", path.c_str()); + } + srs_freep(current); + } + + if (unlink(m3u8.c_str()) < 0) { + srs_warn("dispose unlink path failed. file=%s", m3u8.c_str()); + } + } + + // TODO: FIXME: support hls dispose in HTTP cache. + + srs_trace("gracefully dispose hls %s", req? req->get_stream_url().c_str() : ""); +} + int SrsHlsMuxer::sequence_no() { return _sequence_no; @@ -720,6 +751,9 @@ int SrsHlsMuxer::segment_close(string log_desc) std::string tmp_file = current->full_path + ".tmp"; if (should_write_file) { unlink(tmp_file.c_str()); + if (unlink(tmp_file.c_str()) < 0) { + srs_warn("drop unlink path failed, file=%s.", tmp_file.c_str()); + } } srs_freep(current); @@ -754,7 +788,9 @@ int SrsHlsMuxer::segment_close(string log_desc) SrsHlsSegment* segment = segment_to_remove[i]; if (hls_cleanup) { - unlink(segment->full_path.c_str()); + if (unlink(segment->full_path.c_str()) < 0) { + srs_warn("cleanup unlink path failed, file=%s.", segment->full_path.c_str()); + } } srs_freep(segment); @@ -1085,6 +1121,8 @@ SrsHls::SrsHls() handler = NULL; hls_enabled = false; + hls_can_dispose = false; + last_update_time = 0; codec = new SrsAvcAacCodec(); sample = new SrsCodecSample(); @@ -1109,6 +1147,46 @@ SrsHls::~SrsHls() srs_freep(pprint); } +void SrsHls::dispose() +{ + if (hls_enabled) { + on_unpublish(); + } + + muxer->dispose(); +} + +int SrsHls::cycle() +{ + int ret = ERROR_SUCCESS; + + srs_info("hls cycle for source %d", source->source_id()); + + if (last_update_time <= 0) { + last_update_time = srs_get_system_time_ms(); + } + + if (!_req) { + return ret; + } + + int hls_dispose = _srs_config->get_hls_dispose(_req->vhost) * 1000; + if (srs_get_system_time_ms() - last_update_time <= hls_dispose) { + return ret; + } + last_update_time = srs_get_system_time_ms(); + + if (!hls_can_dispose) { + return ret; + } + hls_can_dispose = false; + + srs_trace("hls cycle to dispose hls %s, timeout=%dms", _req->get_stream_url().c_str(), hls_dispose); + dispose(); + + return ret; +} + int SrsHls::initialize(SrsSource* s, ISrsHlsHandler* h) { int ret = ERROR_SUCCESS; @@ -1127,6 +1205,11 @@ int SrsHls::on_publish(SrsRequest* req) { int ret = ERROR_SUCCESS; + _req = req; + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time_ms(); + // support multiple publish. if (hls_enabled) { return ret; @@ -1144,6 +1227,9 @@ int SrsHls::on_publish(SrsRequest* req) // if enabled, open the muxer. hls_enabled = true; + // ok, the hls can be dispose, or need to be dispose. + hls_can_dispose = true; + // notice the source to get the cached sequence header. // when reload to start hls, hls will never get the sequence header in stream, // use the SrsSource.on_hls_start to push the sequence header to HLS. @@ -1195,6 +1281,9 @@ int SrsHls::on_audio(SrsSharedPtrMessage* shared_audio) if (!hls_enabled) { return ret; } + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time_ms(); SrsSharedPtrMessage* audio = shared_audio->copy(); SrsAutoFree(SrsSharedPtrMessage, audio); @@ -1256,6 +1345,9 @@ int SrsHls::on_video(SrsSharedPtrMessage* shared_video) if (!hls_enabled) { return ret; } + + // update the hls time, for hls_dispose. + last_update_time = srs_get_system_time_ms(); SrsSharedPtrMessage* video = shared_video->copy(); SrsAutoFree(SrsSharedPtrMessage, video); diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index c47e2114e..e2657a1fe 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -259,6 +259,8 @@ private: public: SrsHlsMuxer(); virtual ~SrsHlsMuxer(); +public: + virtual void dispose(); public: virtual int sequence_no(); virtual std::string ts_url(); @@ -379,7 +381,11 @@ private: SrsHlsCache* hls_cache; ISrsHlsHandler* handler; private: + SrsRequest* _req; bool hls_enabled; + bool hls_can_dispose; + int64_t last_update_time; +private: SrsSource* source; SrsAvcAacCodec* codec; SrsCodecSample* sample; @@ -402,6 +408,9 @@ private: public: SrsHls(); virtual ~SrsHls(); +public: + virtual void dispose(); + virtual int cycle(); public: /** * initialize the hls by handler and source. diff --git a/trunk/src/app/srs_app_ingest.cpp b/trunk/src/app/srs_app_ingest.cpp index c2c623439..2a5293e95 100644 --- a/trunk/src/app/srs_app_ingest.cpp +++ b/trunk/src/app/srs_app_ingest.cpp @@ -51,6 +51,11 @@ SrsIngesterFFMPEG::~SrsIngesterFFMPEG() srs_freep(ffmpeg); } +void SrsIngesterFFMPEG::fast_stop() +{ + ffmpeg->fast_stop(); +} + SrsIngester::SrsIngester() { _srs_config->subscribe(this); @@ -159,6 +164,23 @@ int SrsIngester::parse_engines(SrsConfDirective* vhost, SrsConfDirective* ingest return ret; } +void SrsIngester::dispose() +{ + // first, use fast stop to notice all FFMPEG to quit gracefully. + std::vector::iterator it; + for (it = ingesters.begin(); it != ingesters.end(); ++it) { + SrsIngesterFFMPEG* ingester = *it; + ingester->fast_stop(); + } + + if (!ingesters.empty()) { + srs_trace("fast stop all ingesters ok."); + } + + // then, use stop to wait FFMPEG quit one by one and send SIGKILL if needed. + stop(); +} + void SrsIngester::stop() { pthread->stop(); diff --git a/trunk/src/app/srs_app_ingest.hpp b/trunk/src/app/srs_app_ingest.hpp index 7a14a8dac..ede1e5211 100644 --- a/trunk/src/app/srs_app_ingest.hpp +++ b/trunk/src/app/srs_app_ingest.hpp @@ -52,6 +52,9 @@ public: SrsIngesterFFMPEG(SrsFFMPEG* _ffmpeg, std::string _vhost, std::string _id); virtual ~SrsIngesterFFMPEG(); + + // @see SrsFFMPEG.fast_stop(). + virtual void fast_stop(); }; /** @@ -69,6 +72,8 @@ private: public: SrsIngester(); virtual ~SrsIngester(); +public: + virtual void dispose(); public: virtual int start(); virtual void stop(); diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 7c092949a..f9918d56f 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -480,6 +480,7 @@ SrsServer::SrsServer() { signal_reload = false; signal_gmc_stop = false; + signal_gracefully_quit = false; pid_fd = -1; signal_manager = NULL; @@ -519,7 +520,7 @@ void SrsServer::destroy() close_listeners(SrsListenerHttpStream); #ifdef SRS_AUTO_INGEST - ingester->stop(); + ingester->dispose(); #endif #ifdef SRS_AUTO_HTTP_API @@ -555,6 +556,21 @@ void SrsServer::destroy() // and segment fault. } +void SrsServer::dispose() +{ + _srs_config->unsubscribe(this); + +#ifdef SRS_AUTO_INGEST + ingester->dispose(); + srs_trace("gracefully dispose ingesters"); +#endif + + SrsSource::dispose_all(); + srs_trace("gracefully dispose sources"); + + srs_trace("terminate server"); +} + int SrsServer::initialize(ISrsServerCycle* cycle_handler) { int ret = ERROR_SUCCESS; @@ -831,6 +847,7 @@ int SrsServer::cycle() srs_warn("system quit"); #else srs_warn("main cycle terminated, system quit normally."); + dispose(); exit(0); #endif @@ -877,9 +894,9 @@ void SrsServer::on_signal(int signo) return; } - if (signo == SIGTERM) { - srs_trace("user terminate program"); - exit(0); + if (signo == SIGTERM && !signal_gracefully_quit) { + srs_trace("user terminate program, gracefully quit."); + signal_gracefully_quit = true; return; } } @@ -903,7 +920,7 @@ int SrsServer::do_cycle() // the deamon thread, update the time cache while (true) { - if(handler && (ret = handler->on_cycle(conns.size())) != ERROR_SUCCESS){ + if(handler && (ret = handler->on_cycle((int)conns.size())) != ERROR_SUCCESS){ srs_error("cycle handle failed. ret=%d", ret); return ret; } @@ -917,12 +934,18 @@ int SrsServer::do_cycle() for (int i = 0; i < temp_max; i++) { st_usleep(SRS_SYS_CYCLE_INTERVAL * 1000); + + // gracefully quit for SIGINT or SIGTERM. + if (signal_gracefully_quit) { + srs_trace("cleanup for gracefully terminate."); + return ret; + } -// for gperf heap checker, -// @see: research/gperftools/heap-checker/heap_checker.cc -// if user interrupt the program, exit to check mem leak. -// but, if gperf, use reload to ensure main return normally, -// because directly exit will cause core-dump. + // for gperf heap checker, + // @see: research/gperftools/heap-checker/heap_checker.cc + // if user interrupt the program, exit to check mem leak. + // but, if gperf, use reload to ensure main return normally, + // because directly exit will cause core-dump. #ifdef SRS_AUTO_GPERF_MC if (signal_gmc_stop) { srs_warn("gmc got singal to stop server."); @@ -930,6 +953,7 @@ int SrsServer::do_cycle() } #endif + // do reload the config. if (signal_reload) { signal_reload = false; srs_info("get signal reload, to reload the config."); @@ -941,6 +965,11 @@ int SrsServer::do_cycle() srs_trace("reload config success."); } + // notice the stream sources to cycle. + if ((ret = SrsSource::cycle_all()) != ERROR_SUCCESS) { + return ret; + } + // update the cache time if ((i % SRS_SYS_TIME_RESOLUTION_MS_TIMES) == 0) { srs_info("update current time cache."); diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 587adc8f0..d9013a625 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -275,16 +275,22 @@ private: */ bool signal_reload; bool signal_gmc_stop; + bool signal_gracefully_quit; public: SrsServer(); virtual ~SrsServer(); -public: +private: /** * the destroy is for gmc to analysis the memory leak, * if not destroy global/static data, the gmc will warning memory leak. * in service, server never destroy, directly exit when restart. */ virtual void destroy(); + /** + * when SIGTERM, SRS should do cleanup, for example, + * to stop all ingesters, cleanup HLS and dvr. + */ + virtual void dispose(); // server startup workflow, @see run_master() public: virtual int initialize(ISrsServerCycle* cycle_handler); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index bafa2d7a8..2aa5e157e 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -771,6 +771,32 @@ SrsSource* SrsSource::fetch(std::string vhost, std::string app, std::string stre return source; } +void SrsSource::dispose_all() +{ + std::map::iterator it; + for (it = pool.begin(); it != pool.end(); ++it) { + SrsSource* source = it->second; + source->dispose(); + } + return; +} + +int SrsSource::cycle_all() +{ + int ret = ERROR_SUCCESS; + + // TODO: FIXME: support remove dead source for a long time. + std::map::iterator it; + for (it = pool.begin(); it != pool.end(); ++it) { + SrsSource* source = it->second; + if ((ret = source->cycle()) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + void SrsSource::destroy() { std::map::iterator it; @@ -909,6 +935,26 @@ SrsSource::~SrsSource() srs_freep(_req); } +void SrsSource::dispose() +{ +#ifdef SRS_AUTO_HLS + hls->dispose(); +#endif +} + +int SrsSource::cycle() +{ + int ret = ERROR_SUCCESS; + +#ifdef SRS_AUTO_HLS + if ((ret = hls->cycle()) != ERROR_SUCCESS) { + return ret; + } +#endif + + return ret; +} + int SrsSource::initialize(SrsRequest* r, ISrsSourceHandler* h, ISrsHlsHandler* hh) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_source.hpp b/trunk/src/app/srs_app_source.hpp index 3c83e1163..f7a75d4ac 100644 --- a/trunk/src/app/srs_app_source.hpp +++ b/trunk/src/app/srs_app_source.hpp @@ -410,6 +410,11 @@ public: * get the exists source by stream info(vhost, app, stream), NULL when not exists. */ static SrsSource* fetch(std::string vhost, std::string app, std::string stream); + /** + * dispose and cycle all sources. + */ + static void dispose_all(); + static int cycle_all(); /** * when system exit, destroy the sources, * for gmc to analysis mem leaks. @@ -486,6 +491,9 @@ private: public: SrsSource(); virtual ~SrsSource(); +public: + virtual void dispose(); + virtual int cycle(); // initialize, get and setter. public: /** diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 31e4681a7..2f8b1248e 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include #ifdef SRS_OSX #include @@ -43,6 +44,9 @@ using namespace std; #include #include +// the longest time to wait for a process to quit. +#define SRS_PROCESS_QUIT_TIMEOUT_MS 1000 + int srs_socket_connect(string server, int port, int64_t timeout, st_netfd_t* pstfd) { int ret = ERROR_SUCCESS; @@ -222,6 +226,62 @@ void srs_parse_endpoint(string ip_port, string& ip, int& port) port = ::atoi(the_port.c_str()); } +int srs_kill_forced(int& pid) +{ + int ret = ERROR_SUCCESS; + + if (pid <= 0) { + return ret; + } + + // first, try kill by SIGTERM. + if (kill(pid, SIGTERM) < 0) { + return ERROR_SYSTEM_KILL; + } + + // wait to quit. + srs_trace("send SIGTERM to pid=%d", pid); + for (int i = 0; i < SRS_PROCESS_QUIT_TIMEOUT_MS / 10; i++) { + int status = 0; + pid_t qpid = -1; + if ((qpid = waitpid(pid, &status, WNOHANG)) < 0) { + return ERROR_SYSTEM_KILL; + } + + // 0 is not quit yet. + if (qpid == 0) { + st_usleep(10 * 1000); + continue; + } + + // killed, set pid to -1. + srs_trace("SIGTERM stop process pid=%d ok.", pid); + pid = -1; + + return ret; + } + + // then, try kill by SIGKILL. + if (kill(pid, SIGKILL) < 0) { + return ERROR_SYSTEM_KILL; + } + + // wait for the process to quit. + // for example, ffmpeg will gracefully quit if signal is: + // 1) SIGHUP 2) SIGINT 3) SIGQUIT + // other signals, directly exit(123), for example: + // 9) SIGKILL 15) SIGTERM + int status = 0; + if (waitpid(pid, &status, 0) < 0) { + return ERROR_SYSTEM_KILL; + } + + srs_trace("SIGKILL stop process pid=%d ok.", pid); + pid = -1; + + return ret; +} + static SrsRusage _srs_system_rusage; SrsRusage::SrsRusage() @@ -422,7 +482,7 @@ void srs_update_proc_stat() // @see https://github.com/simple-rtmp-server/srs/issues/397 static int user_hz = 0; if (user_hz <= 0) { - user_hz = sysconf(_SC_CLK_TCK); + user_hz = (int)sysconf(_SC_CLK_TCK); srs_trace("USER_HZ=%d", user_hz); srs_assert(user_hz > 0); } @@ -646,12 +706,12 @@ void srs_update_disk_stat() if (o.pgpgin > 0 && r.pgpgin > o.pgpgin && duration_ms > 0) { // KBps = KB * 1000 / ms = KB/s - r.in_KBps = (r.pgpgin - o.pgpgin) * 1000 / duration_ms; + r.in_KBps = (int)((r.pgpgin - o.pgpgin) * 1000 / duration_ms); } if (o.pgpgout > 0 && r.pgpgout > o.pgpgout && duration_ms > 0) { // KBps = KB * 1000 / ms = KB/s - r.out_KBps = (r.pgpgout - o.pgpgout) * 1000 / duration_ms; + r.out_KBps = (int)((r.pgpgout - o.pgpgout) * 1000 / duration_ms); } } @@ -771,8 +831,8 @@ SrsCpuInfo* srs_get_cpuinfo() // initialize cpu info. cpu = new SrsCpuInfo(); cpu->ok = true; - cpu->nb_processors = sysconf(_SC_NPROCESSORS_CONF); - cpu->nb_processors_online = sysconf(_SC_NPROCESSORS_ONLN); + cpu->nb_processors = (int)sysconf(_SC_NPROCESSORS_CONF); + cpu->nb_processors_online = (int)sysconf(_SC_NPROCESSORS_ONLN); return cpu; } diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index e86519db0..cd95b9781 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -80,6 +80,14 @@ extern std::string srs_path_build_timestamp(std::string template_path); extern void srs_parse_endpoint(std::string ip_port, std::string& ip, std::string& port); extern void srs_parse_endpoint(std::string ip_port, std::string& ip, int& port); +/** + * kill the pid by SIGINT, then wait to quit, + * kill the pid by SIGKILL again when exceed the timeout. + * @param pid the pid to kill. ignore for -1. set to -1 when killed. + * @return an int error code. + */ +extern int srs_kill_forced(int& pid); + // current process resouce usage. // @see: man getrusage class SrsRusage diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index dec0f3f72..5f43da1d0 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -96,6 +96,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_TIME 1055 #define ERROR_SYSTEM_DIR_EXISTS 1056 #define ERROR_SYSTEM_CREATE_DIR 1057 +#define ERROR_SYSTEM_KILL 1058 /////////////////////////////////////////////////////// // RTMP protocol error.