@@ -3,6 +3,7 @@ package io.nekohasekai.sfa.bg
33import android.annotation.SuppressLint
44import android.net.NetworkCapabilities
55import android.os.Build
6+ import android.os.ParcelFileDescriptor
67import android.os.Process
78import android.system.OsConstants
89import android.util.Log
@@ -15,12 +16,17 @@ import io.nekohasekai.libbox.NeighborEntryIterator
1516import io.nekohasekai.libbox.NeighborUpdateListener
1617import io.nekohasekai.libbox.NetworkInterfaceIterator
1718import io.nekohasekai.libbox.PlatformInterface
19+ import io.nekohasekai.libbox.PlatformUser
20+ import io.nekohasekai.libbox.ShellSession
1821import io.nekohasekai.libbox.StringIterator
1922import io.nekohasekai.libbox.TunOptions
2023import io.nekohasekai.libbox.WIFIState
2124import io.nekohasekai.sfa.Application
25+ import io.nekohasekai.sfa.ktx.toList
26+ import io.nekohasekai.sfa.ktx.toStringIterator
2227import kotlinx.coroutines.Dispatchers
2328import kotlinx.coroutines.runBlocking
29+ import java.io.File
2430import java.net.Inet6Address
2531import java.net.InetSocketAddress
2632import java.net.InterfaceAddress
@@ -205,6 +211,99 @@ interface PlatformInterfaceWrapper : PlatformInterface {
205211 }
206212 }
207213
214+ override fun usePlatformShell (): Boolean = true
215+
216+ override fun checkPlatformShell () {
217+ val available = RootClient .rootAvailable.value ? : runBlocking(Dispatchers .IO ) {
218+ RootClient .checkRootAvailable()
219+ }
220+ if (! available) {
221+ error(" missing root permission" )
222+ }
223+ }
224+
225+ override fun openShellSession (
226+ user : PlatformUser ? ,
227+ command : String? ,
228+ environ : StringIterator ? ,
229+ term : String? ,
230+ rows : Int ,
231+ cols : Int ,
232+ ): ShellSession {
233+ user!!
234+ val envList = environ?.toList().orEmpty()
235+ if (user.uid == Process .myUid()) {
236+ val resolved = ResolvedUser (user.username, user.uid, user.gid, user.homeDir)
237+ val shell = UserResolver .findShell(resolved)
238+ val shellEnv = buildBasicEnvironment(envList.toTypedArray(), shell, resolved.homeDir, term)
239+ val args = if (command.isNullOrEmpty()) {
240+ arrayOf(" -" + File (shell).name)
241+ } else {
242+ arrayOf(File (shell).name, " -c" , command)
243+ }
244+ val argsIter = args.asIterable().toStringIterator()
245+ val envIter = shellEnv.asIterable().toStringIterator()
246+ return if (term.isNullOrEmpty()) {
247+ Libbox .openNativePipeSession(
248+ shell,
249+ resolved.homeDir,
250+ argsIter,
251+ envIter,
252+ - 1 ,
253+ - 1 ,
254+ null ,
255+ )
256+ } else {
257+ Libbox .openNativeShellSession(
258+ shell,
259+ resolved.homeDir,
260+ argsIter,
261+ envIter,
262+ term,
263+ rows,
264+ cols,
265+ - 1 ,
266+ - 1 ,
267+ null ,
268+ )
269+ }
270+ }
271+ val rootSession = runBlocking(Dispatchers .IO ) {
272+ RootClient .openShellSession(
273+ user.username,
274+ command,
275+ envList.toTypedArray(),
276+ term,
277+ rows,
278+ cols,
279+ )
280+ }
281+ return RootShellSessionWrapper (rootSession)
282+ }
283+
284+ override fun readSystemSSHHostKey (): io.nekohasekai.libbox.StringBox {
285+ error(" not supported" )
286+ }
287+
288+ override fun lookupSFTPServer (): io.nekohasekai.libbox.StringBox {
289+ val path = runBlocking(Dispatchers .IO ) {
290+ RootClient .lookupSFTPServer()
291+ }
292+ val result = io.nekohasekai.libbox.StringBox ()
293+ result.value = path
294+ return result
295+ }
296+
297+ override fun lookupUser (username : String? ): io.nekohasekai.libbox.PlatformUser {
298+ val resolved = UserResolver .resolve(Application .packageManager, username!! )
299+ val platformUser = io.nekohasekai.libbox.PlatformUser ()
300+ platformUser.username = resolved.packageName
301+ platformUser.uid = resolved.uid
302+ platformUser.gid = resolved.gid
303+ platformUser.homeDir = resolved.homeDir
304+ return platformUser
305+ }
306+
208307 override fun registerMyInterface (name : String? ) {
209308 }
210309
@@ -216,6 +315,29 @@ interface PlatformInterfaceWrapper : PlatformInterface {
216315 }
217316 }
218317
318+ private class RootShellSessionWrapper (
319+ private val rootSession : IRootShellSession ,
320+ ) : ShellSession {
321+ private val masterPfd: ParcelFileDescriptor = rootSession.masterFD
322+
323+ override fun masterFD (): Int = masterPfd.fd
324+
325+ override fun resize (rows : Int , cols : Int ) {
326+ rootSession.resize(rows, cols)
327+ }
328+
329+ override fun signal (signal : Int ) {
330+ rootSession.signal(signal)
331+ }
332+
333+ override fun waitExit (): Int = rootSession.waitFor()
334+
335+ override fun close () {
336+ masterPfd.close()
337+ rootSession.close()
338+ }
339+ }
340+
219341 private class NeighborEntryArray (private val iterator : Iterator <LibboxNeighborEntry >) : NeighborEntryIterator {
220342 override fun hasNext (): Boolean = iterator.hasNext()
221343
0 commit comments