44 * Copyright 2008 Andrew Riedi
55 * Copyright 2010 Andrew Nguyen
66 * Copyright 2020 He Yang
7+ * Copyright 2025 Hermès Bélusca-Maïto
78 *
89 * This library is free software; you can redistribute it and/or
910 * modify it under the terms of the GNU Lesser General Public
@@ -301,9 +302,9 @@ static void taskkill_message_print_process(int msg, unsigned int index)
301302 * obtained from the system, is such that any child process P[j] of a given
302303 * parent process P[i] is enumerated *AFTER* its parent (i.e. i < j).
303304 *
304- * Because of these facts, the ReactOS recursive method is employed instead.
305- * Note however that the Wine method (below) has been adapted for ease of
306- * usage and comparison with that of ReactOS.
305+ * Because of these facts, the ReactOS method is employed instead.
306+ * Note however that the Wine method (below) has been adapted for
307+ * ease of usage and comparison with that of ReactOS.
307308 */
308309
309310static BOOL find_parent (unsigned int process_index , unsigned int * parent_index )
@@ -376,65 +377,99 @@ static void mark_child_processes(void)
376377static BOOL get_pid_creation_time (DWORD pid , FILETIME * time )
377378{
378379 HANDLE process ;
379- FILETIME t1 = { 0 }, t2 = { 0 }, t3 = { 0 };
380+ FILETIME t1 , t2 , t3 ;
381+ BOOL success ;
380382
381383 process = OpenProcess (PROCESS_QUERY_INFORMATION , FALSE, pid );
382384 if (!process )
383- {
384- return FALSE;
385- }
386-
387- if (!GetProcessTimes (process , time , & t1 , & t2 , & t3 ))
388- {
389- CloseHandle (process );
390385 return FALSE;
391- }
392386
387+ success = GetProcessTimes (process , time , & t1 , & t2 , & t3 );
393388 CloseHandle (process );
394- return TRUE;
389+
390+ return success ;
395391}
396392
397- static void send_close_messages_tree (DWORD ppid )
393+ static void queue_children (DWORD ppid )
398394{
399- FILETIME parent_creation_time = { 0 };
400- HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS , 0 );
401- PROCESSENTRY32W pe = { 0 };
402- pe .dwSize = sizeof (PROCESSENTRY32W );
395+ FILETIME parent_creation_time ;
396+ unsigned int i ;
403397
404- if (!get_pid_creation_time (ppid , & parent_creation_time ) || !h )
405- {
406- CloseHandle (h );
398+ if (!get_pid_creation_time (ppid , & parent_creation_time ))
407399 return ;
408- }
409400
410- if ( Process32FirstW ( h , & pe ) )
401+ for ( i = 0 ; i < process_count ; ++ i )
411402 {
412- do
413- {
414- FILETIME child_creation_time = { 0 };
415- struct pid_close_info info = { pe .th32ProcessID };
403+ FILETIME child_creation_time ;
404+ DWORD pid ;
416405
417- if (!get_pid_creation_time (pe .th32ProcessID , & child_creation_time ))
418- {
419- continue ;
420- }
406+ // Ignore processes already marked for termination
407+ if (process_list [i ].matched )
408+ continue ;
421409
422- // Compare creation time to avoid reuse PID, thanks to @ThFabba
423- if (pe .th32ParentProcessID == ppid &&
424- CompareFileTime (& parent_creation_time , & child_creation_time ) < 0 )
425- {
426- // Use recursion to browse all child processes
427- send_close_messages_tree (pe .th32ProcessID );
428- EnumWindows (pid_enum_proc , (LPARAM )& info );
429- if (info .found )
430- {
431- taskkill_message_printfW (STRING_CLOSE_CHILD , pe .th32ProcessID , ppid );
432- }
433- }
434- } while (Process32NextW (h , & pe ));
410+ // Prevent self-termination if we are in the process tree
411+ pid = process_list [i ].p .th32ProcessID ;
412+ if (pid == self_pid )
413+ continue ;
414+
415+ if (!get_pid_creation_time (pid , & child_creation_time ))
416+ continue ;
417+
418+ // Compare creation time to avoid PID reuse cases
419+ if (process_list [i ].p .th32ParentProcessID == ppid &&
420+ CompareFileTime (& parent_creation_time , & child_creation_time ) < 0 )
421+ {
422+ // Process marked for termination
423+ WINE_TRACE ("Adding child %04lx.\n" , pid );
424+ process_list [i ].matched = TRUE;
425+ pkill_list [pkill_size ++ ] = i ;
426+ }
435427 }
428+ }
436429
437- CloseHandle (h );
430+ /*
431+ * Based on the root processes to terminate, we perform a level order traversal
432+ * (Breadth First Search) of the corresponding process trees, building the list
433+ * of all processes and children to terminate,
434+ * This allows terminating the processes, starting from parents down to their
435+ * children. Note that this is in the reverse order than what Windows' taskkill
436+ * does. The reason why we chose to do the reverse, is because there exist
437+ * (parent) processes that detect whether their children are terminated, and
438+ * if so, attempt to restart their terminated children. We want to avoid this
439+ * scenario in order to ensure no extra processes get started, while the user
440+ * wanted to terminate them.
441+ */
442+ static void mark_child_processes (void )
443+ {
444+ /*
445+ * The temporary FIFO queue for BFS (starting empty), is embedded
446+ * inside the result list. The queue resides between the [front, end)
447+ * indices (if front == end, the queue is empty), and moves down
448+ * through the result list, generating the sought values in order.
449+ */
450+ unsigned int front = 0 ; // end = 0; given by pkill_size
451+
452+ /* The root processes have already been
453+ * enqueued in pkill_list[0..pkill_size] */
454+
455+ /* Now, find and enqueue the children processes */
456+ while (pkill_size - front > 0 )
457+ {
458+ /* Begin search at the next level */
459+ unsigned int len = pkill_size - front ;
460+ unsigned int i ;
461+ for (i = 0 ; i < len ; ++ i )
462+ {
463+ /* Standard BFS would pop the element from the front of
464+ * the queue and push it to the end of the result list.
465+ * In our case, everything is already correctly positioned
466+ * so that we can just simply emulate queue front popping. */
467+ DWORD pid = process_list [pkill_list [front ++ ]].p .th32ProcessID ;
468+
469+ /* Enqueue the children processes */
470+ queue_children (pid ); // Updates pkill_size accordingly.
471+ }
472+ }
438473}
439474
440475#endif // __REACTOS__
@@ -461,17 +496,6 @@ static int send_close_messages(void)
461496#endif
462497
463498 info .pid = process_list [i ].p .th32ProcessID ;
464- #ifdef __REACTOS__
465- if (info .pid == self_pid )
466- {
467- taskkill_message (STRING_SELF_TERMINATION );
468- status_code = 1 ;
469- continue ;
470- }
471- // Send close messages to child first
472- if (kill_child_processes )
473- send_close_messages_tree (info .pid );
474- #endif
475499 process_name = process_list [i ].p .szExeFile ;
476500 info .found = FALSE;
477501 WINE_TRACE ("Terminating pid %04lx.\n" , info .pid );
@@ -493,64 +517,6 @@ static int send_close_messages(void)
493517 return status_code ;
494518}
495519
496- #ifdef __REACTOS__
497-
498- static void terminate_process_tree (DWORD ppid )
499- {
500- FILETIME parent_creation_time = { 0 };
501- HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS , 0 );
502- PROCESSENTRY32W pe = { 0 };
503- pe .dwSize = sizeof (PROCESSENTRY32W );
504-
505- if (!get_pid_creation_time (ppid , & parent_creation_time ) || !h )
506- {
507- CloseHandle (h );
508- return ;
509- }
510-
511- if (Process32FirstW (h , & pe ))
512- {
513- do
514- {
515- FILETIME child_creation_time = { 0 };
516-
517- if (!get_pid_creation_time (pe .th32ProcessID , & child_creation_time ))
518- {
519- continue ;
520- }
521-
522- // Compare creation time to avoid reuse PID, thanks to @ThFabba
523- if (pe .th32ParentProcessID == ppid &&
524- CompareFileTime (& parent_creation_time , & child_creation_time ) < 0 )
525- {
526- HANDLE process ;
527-
528- // Use recursion to browse all child processes
529- terminate_process_tree (pe .th32ProcessID );
530- process = OpenProcess (PROCESS_TERMINATE , FALSE, pe .th32ProcessID );
531- if (!process )
532- {
533- continue ;
534- }
535-
536- if (!TerminateProcess (process , 1 ))
537- {
538- taskkill_message_printfW (STRING_TERM_CHILD_FAILED , pe .th32ProcessID , ppid );
539- CloseHandle (process );
540- continue ;
541- }
542-
543- taskkill_message_printfW (STRING_TERM_CHILD , pe .th32ProcessID , ppid );
544- CloseHandle (process );
545- }
546- } while (Process32NextW (h , & pe ));
547- }
548-
549- CloseHandle (h );
550- }
551-
552- #endif // __REACTOS__
553-
554520static int terminate_processes (void )
555521{
556522 const WCHAR * process_name ;
@@ -574,17 +540,6 @@ static int terminate_processes(void)
574540#endif
575541
576542 pid = process_list [i ].p .th32ProcessID ;
577- #ifdef __REACTOS__
578- if (pid == self_pid )
579- {
580- taskkill_message (STRING_SELF_TERMINATION );
581- status_code = 1 ;
582- continue ;
583- }
584- // Terminate child first
585- if (kill_child_processes )
586- terminate_process_tree (pid );
587- #endif
588543 process_name = process_list [i ].p .szExeFile ;
589544 process = OpenProcess (PROCESS_TERMINATE , FALSE, pid );
590545 if (!process )
@@ -679,7 +634,7 @@ static BOOL process_arguments(int argc, WCHAR* argv[])
679634 {
680635 case OP_PARAM_FORCE_TERMINATE :
681636 {
682- if (force_termination == TRUE )
637+ if (force_termination )
683638 {
684639 // -f already specified
685640 taskkill_message_printfW (STRING_PARAM_TOO_MUCH , argv [i ], 1 );
@@ -725,7 +680,7 @@ static BOOL process_arguments(int argc, WCHAR* argv[])
725680 }
726681 case OP_PARAM_HELP :
727682 {
728- if (has_help == TRUE )
683+ if (has_help )
729684 {
730685 // -? already specified
731686 taskkill_message_printfW (STRING_PARAM_TOO_MUCH , argv [i ], 1 );
@@ -737,7 +692,7 @@ static BOOL process_arguments(int argc, WCHAR* argv[])
737692 }
738693 case OP_PARAM_TERMINATE_CHILD :
739694 {
740- if (kill_child_processes == TRUE )
695+ if (kill_child_processes )
741696 {
742697 // -t already specified
743698 taskkill_message_printfW (STRING_PARAM_TOO_MUCH , argv [i ], 1 );
@@ -772,7 +727,7 @@ static BOOL process_arguments(int argc, WCHAR* argv[])
772727 exit (0 );
773728 }
774729 }
775- else if (( !has_im ) && ( !has_pid ) ) // has_help == FALSE
730+ else if (!has_im && !has_pid ) // has_help == FALSE
776731 {
777732 // both has_im and has_pid are missing (maybe -fi option is missing too, if implemented later)
778733 taskkill_message (STRING_MISSING_OPTION );
@@ -893,10 +848,8 @@ int wmain(int argc, WCHAR *argv[])
893848
894849 for (i = 0 ; i < task_count ; ++ i )
895850 mark_task_process (task_list [i ], & search_status );
896- #ifndef __REACTOS__
897851 if (kill_child_processes )
898852 mark_child_processes ();
899- #endif
900853 if (force_termination )
901854 terminate_status = terminate_processes ();
902855 else
0 commit comments