1+ using Microsoft . Extensions . Logging ;
2+ using System ;
3+ using System . Diagnostics ;
4+ using System . Runtime . InteropServices ;
5+ using System . Text . RegularExpressions ;
6+ using System . Collections . Generic ;
7+ using System . Threading . Tasks ;
8+
9+ namespace VueCliMiddleware
10+ {
11+ public static class PidUtils
12+ {
13+
14+ const string ssPidRegex = @"(?:^|"",|"",pid=)(\d+)" ;
15+ const string portRegex = @"[^]*[.:](\\d+)$" ;
16+
17+ public static int GetPortPid ( ushort port )
18+ {
19+ int pidOut = - 1 ;
20+
21+ int portColumn = 1 ; // windows
22+ int pidColumn = 4 ; // windows
23+ string pidRegex = null ;
24+
25+ List < string [ ] > results = null ;
26+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
27+ {
28+ results = RunProcessReturnOutputSplit ( "netstat" , "-anv -p tcp" ) ;
29+ results . AddRange ( RunProcessReturnOutputSplit ( "netstat" , "-anv -p udp" ) ) ;
30+ portColumn = 3 ;
31+ pidColumn = 8 ;
32+ }
33+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
34+ {
35+ results = RunProcessReturnOutputSplit ( "ss" , "-tunlp" ) ;
36+ portColumn = 4 ;
37+ pidColumn = 6 ;
38+ pidRegex = ssPidRegex ;
39+ }
40+ else
41+ {
42+ results = RunProcessReturnOutputSplit ( "netstat" , "-ano" ) ;
43+ }
44+
45+
46+ foreach ( var line in results )
47+ {
48+ if ( line . Length <= portColumn || line . Length <= pidColumn ) continue ;
49+ try
50+ {
51+ // split lines to words
52+ var portMatch = Regex . Match ( line [ portColumn ] , $ "[.:]({ port } )") ;
53+ if ( portMatch . Success )
54+ {
55+ int portValue = int . Parse ( portMatch . Groups [ 1 ] . Value ) ;
56+
57+ if ( pidRegex == null )
58+ {
59+ pidOut = int . Parse ( line [ pidColumn ] ) ;
60+ return pidOut ;
61+ }
62+ else
63+ {
64+ var pidMatch = Regex . Match ( line [ pidColumn ] , pidRegex ) ;
65+ if ( pidMatch . Success )
66+ {
67+ pidOut = int . Parse ( pidMatch . Groups [ 1 ] . Value ) ;
68+ }
69+ }
70+ }
71+ }
72+ catch ( Exception ex )
73+ {
74+ // ignore line error
75+ }
76+ }
77+
78+ return pidOut ;
79+ }
80+
81+ private static List < string [ ] > RunProcessReturnOutputSplit ( string fileName , string arguments )
82+ {
83+ string result = RunProcessReturnOutput ( fileName , arguments ) ;
84+ if ( result == null ) return null ;
85+
86+ string [ ] lines = result . Split ( new string [ ] { Environment . NewLine } , StringSplitOptions . RemoveEmptyEntries ) ;
87+ var lineWords = new List < string [ ] > ( ) ;
88+ foreach ( var line in lines )
89+ {
90+ lineWords . Add ( line . Split ( new char [ ] { ' ' } , StringSplitOptions . RemoveEmptyEntries ) ) ;
91+ }
92+ return lineWords ;
93+ }
94+
95+ private static string RunProcessReturnOutput ( string fileName , string arguments )
96+ {
97+ Process process = null ;
98+ try
99+ {
100+ var si = new ProcessStartInfo ( fileName , arguments )
101+ {
102+ UseShellExecute = false ,
103+ RedirectStandardOutput = true ,
104+ RedirectStandardError = true ,
105+ CreateNoWindow = true
106+ } ;
107+
108+ process = Process . Start ( si ) ;
109+ var stdOutT = process . StandardOutput . ReadToEndAsync ( ) ;
110+ var stdErrorT = process . StandardError . ReadToEndAsync ( ) ;
111+ if ( ! process . WaitForExit ( 10000 ) )
112+ {
113+ try { process ? . Kill ( ) ; } catch { }
114+ }
115+
116+ if ( Task . WaitAll ( new Task [ ] { stdOutT , stdErrorT } , 10000 ) )
117+ {
118+ // if success, return data
119+ return ( stdOutT . Result + Environment . NewLine + stdErrorT . Result ) . Trim ( ) ;
120+ }
121+ return null ;
122+ }
123+ catch ( Exception ex )
124+ {
125+ return null ;
126+ }
127+ finally
128+ {
129+ process ? . Close ( ) ;
130+ }
131+ }
132+
133+ public static bool Kill ( string process , bool ignoreCase = true , bool force = false , bool tree = true )
134+ {
135+ var args = new List < string > ( ) ;
136+ try
137+ {
138+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
139+ {
140+ if ( force ) { args . Add ( "-9" ) ; }
141+ if ( ignoreCase ) { args . Add ( "-i" ) ; }
142+ args . Add ( process ) ;
143+ RunProcessReturnOutput ( "pkill" , string . Join ( " " , args ) ) ;
144+ }
145+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
146+ {
147+ if ( force ) { args . Add ( "-9" ) ; }
148+ if ( ignoreCase ) { args . Add ( "-I" ) ; }
149+ args . Add ( process ) ;
150+ RunProcessReturnOutput ( "killall" , string . Join ( " " , args ) ) ;
151+ }
152+ else
153+ {
154+ if ( force ) { args . Add ( "/f" ) ; }
155+ if ( tree ) { args . Add ( "/T" ) ; }
156+ args . Add ( "/im" ) ;
157+ args . Add ( process ) ;
158+ return RunProcessReturnOutput ( "taskkill" , string . Join ( " " , args ) ) ? . StartsWith ( "SUCCESS" ) ?? false ;
159+ }
160+ return true ;
161+ }
162+ catch ( Exception )
163+ {
164+
165+ }
166+ return false ;
167+ }
168+
169+ public static bool Kill ( int pid , bool force = false , bool tree = true )
170+ {
171+ var args = new List < string > ( ) ;
172+ try
173+ {
174+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
175+ {
176+ if ( force ) { args . Add ( "-9" ) ; }
177+ RunProcessReturnOutput ( "kill" , "" ) ;
178+ }
179+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
180+ {
181+ if ( force ) { args . Add ( "-9" ) ; }
182+ RunProcessReturnOutput ( "kill" , "" ) ;
183+ }
184+ else
185+ {
186+ if ( force ) { args . Add ( "/f" ) ; }
187+ if ( tree ) { args . Add ( "/T" ) ; }
188+ args . Add ( "/PID" ) ;
189+ args . Add ( pid . ToString ( ) ) ;
190+ return RunProcessReturnOutput ( "taskkill" , string . Join ( " " , args ) ) ? . StartsWith ( "SUCCESS" ) ?? false ;
191+ }
192+ return true ;
193+ }
194+ catch ( Exception ex )
195+ {
196+ }
197+ return false ;
198+ }
199+
200+ public static bool KillPort ( ushort port , bool force = false , bool tree = true ) => Kill ( GetPortPid ( port ) , force : force , tree : tree ) ;
201+
202+ }
203+
204+
205+ }
0 commit comments