# HG changeset patch # User Bram Moolenaar # Date 1615408203 -3600 # Node ID cdeec1389c8c1a8b114bf03732cb782f00e63daa # Parent 03ee5ad8e15a4d8746ac38bb61cb4ce10389a5c4 patch 8.2.2586: process id may be invalid Commit: https://github.com/vim/vim/commit/f52f0606ed9ea19bcfc3a8343af9958f2d99eaf7 Author: Bram Moolenaar Date: Wed Mar 10 21:26:37 2021 +0100 patch 8.2.2586: process id may be invalid Problem: Process id may be invalid. Solution: Use sysinfo.uptime to check for recent reboot. (suggested by Hugo van der Sanden, closes #7947) diff --git a/src/auto/configure b/src/auto/configure --- a/src/auto/configure +++ b/src/auto/configure @@ -13918,6 +13918,35 @@ else fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysinfo.uptime" >&5 +$as_echo_n "checking for sysinfo.uptime... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + struct sysinfo sinfo; + long ut; + + (void)sysinfo(&sinfo); + ut = sinfo.uptime; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSINFO_UPTIME 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysconf" >&5 $as_echo_n "checking for sysconf... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext diff --git a/src/config.h.in b/src/config.h.in --- a/src/config.h.in +++ b/src/config.h.in @@ -213,6 +213,7 @@ #undef HAVE_SYSCTL #undef HAVE_SYSINFO #undef HAVE_SYSINFO_MEM_UNIT +#undef HAVE_SYSINFO_UPTIME #undef HAVE_TGETENT #undef HAVE_TOWLOWER #undef HAVE_TOWUPPER diff --git a/src/configure.ac b/src/configure.ac --- a/src/configure.ac +++ b/src/configure.ac @@ -4095,6 +4095,20 @@ AC_TRY_COMPILE( AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO_MEM_UNIT), AC_MSG_RESULT(no)) +dnl struct sysinfo may have the uptime field or not +AC_MSG_CHECKING(for sysinfo.uptime) +AC_TRY_COMPILE( +[#include +#include ], +[ struct sysinfo sinfo; + long ut; + + (void)sysinfo(&sinfo); + ut = sinfo.uptime; + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO_UPTIME), + AC_MSG_RESULT(no)) + dnl sysconf() may exist but not support what we want to use AC_MSG_CHECKING(for sysconf) AC_TRY_COMPILE( diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1860,6 +1860,7 @@ EXTERN int nfa_fail_for_testing INIT(= EXTERN int no_query_mouse_for_testing INIT(= FALSE); EXTERN int ui_delay_for_testing INIT(= 0); EXTERN int reset_term_props_on_termresponse INIT(= FALSE); +EXTERN long override_sysinfo_uptime INIT(= -1); EXTERN int in_free_unref_items INIT(= FALSE); #endif diff --git a/src/memline.c b/src/memline.c --- a/src/memline.c +++ b/src/memline.c @@ -1080,6 +1080,32 @@ add_b0_fenc( } } +#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME) +# include +#endif + +/* + * Return TRUE if the process with number "b0p->b0_pid" is still running. + * "swap_fname" is the name of the swap file, if it's from before a reboot then + * the result is FALSE; + */ + static int +swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED) +{ +#ifdef HAVE_SYSINFO_UPTIME + stat_T st; + struct sysinfo sinfo; + + // If the system rebooted after when the swap file was written then the + // process can't be running now. + if (mch_stat((char *)swap_fname, &st) != -1 + && sysinfo(&sinfo) == 0 + && st.st_mtime < time(NULL) - (override_sysinfo_uptime >= 0 + ? override_sysinfo_uptime : sinfo.uptime)) + return FALSE; +#endif + return mch_process_running(char_to_long(b0p->b0_pid)); +} /* * Try to recover curbuf from the .swp file. @@ -1692,7 +1718,7 @@ ml_recover(int checkext) msg(_("Recovery completed. Buffer contents equals file contents.")); msg_puts(_("\nYou may want to delete the .swp file now.")); #if defined(UNIX) || defined(MSWIN) - if (mch_process_running(char_to_long(b0p->b0_pid))) + if (swapfile_process_running(b0p, fname_used)) { // Warn there could be an active Vim on the same file, the user may // want to kill it. @@ -2170,7 +2196,7 @@ swapfile_info(char_u *fname) msg_puts(_("\n process ID: ")); msg_outnum(char_to_long(b0.b0_pid)); #if defined(UNIX) || defined(MSWIN) - if (mch_process_running(char_to_long(b0.b0_pid))) + if (swapfile_process_running(&b0, fname)) { msg_puts(_(" (STILL RUNNING)")); # ifdef HAVE_PROCESS_STILL_RUNNING @@ -2213,9 +2239,6 @@ swapfile_unchanged(char_u *fname) int fd; struct block0 b0; int ret = TRUE; -#if defined(UNIX) || defined(MSWIN) - long pid; -#endif // must be able to stat the swap file if (mch_stat((char *)fname, &st) == -1) @@ -2258,8 +2281,7 @@ swapfile_unchanged(char_u *fname) } // process must be known and not be running - pid = char_to_long(b0.b0_pid); - if (pid == 0L || mch_process_running(pid)) + if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) ret = FALSE; #endif diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -1,5 +1,7 @@ " Test :recover +source check.vim + func Test_recover_root_dir() " This used to access invalid memory. split Xtest @@ -21,6 +23,21 @@ func Test_recover_root_dir() set dir& endfunc +" Make a copy of the current swap file to "Xswap". +" Return the name of the swap file. +func CopySwapfile() + preserve + " get the name of the swap file + let swname = split(execute("swapname"))[0] + let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') + " make a copy of the swap file in Xswap + set binary + exe 'sp ' . swname + w! Xswap + set nobinary + return swname +endfunc + " Inserts 10000 lines with text to fill the swap file with two levels of pointer " blocks. Then recovers from the swap file and checks all text is restored. " @@ -37,15 +54,9 @@ func Test_swap_file() let i += 1 endwhile $delete - preserve - " get the name of the swap file - let swname = split(execute("swapname"))[0] - let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') - " make a copy of the swap file in Xswap - set binary - exe 'sp ' . swname - w! Xswap - set nobinary + + let swname = CopySwapfile() + new only! bwipe! Xtest @@ -67,4 +78,52 @@ func Test_swap_file() enew! | only endfunc +func Test_nocatch_process_still_running() + " assume Unix means sysinfo.uptime can be used + CheckUnix + CheckNotGui + + " don't intercept existing swap file here + au! SwapExists + + " Edit a file and grab its swapfile. + edit Xswaptest + call setline(1, ['a', 'b', 'c']) + let swname = CopySwapfile() + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + call rename('Xswap', swname) + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_match('file name: .*Xswaptest', editOutput) + call assert_match('process ID: \d* (STILL RUNNING)', editOutput) + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + " pretend we rebooted + call test_override("uptime", 0) + sleep 1 + + call rename('Xswap', swname) + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_notmatch('(STILL RUNNING)', editOutput) + + call test_override("ALL", 0) + call delete(swname) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testing.c b/src/testing.c --- a/src/testing.c +++ b/src/testing.c @@ -970,6 +970,8 @@ f_test_override(typval_T *argvars, typva ui_delay_for_testing = val; else if (STRCMP(name, (char_u *)"term_props") == 0) reset_term_props_on_termresponse = val; + else if (STRCMP(name, (char_u *)"uptime") == 0) + override_sysinfo_uptime = val; else if (STRCMP(name, (char_u *)"ALL") == 0) { disable_char_avail_for_testing = FALSE; @@ -979,6 +981,7 @@ f_test_override(typval_T *argvars, typva no_query_mouse_for_testing = FALSE; ui_delay_for_testing = 0; reset_term_props_on_termresponse = FALSE; + override_sysinfo_uptime = -1; if (save_starting >= 0) { starting = save_starting; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2586, +/**/ 2585, /**/ 2584,