用智能手机控制你的 Linux 系统
手机技术最近取得了长足的进步。个人电脑和手持设备之间的差距正在缩小。我一直听到“PC 的末日”这种说法,并且这句话可能有些道理。但是,我相信我们中的许多人将继续需要访问更大、更强大的计算机,这些计算机太大而无法放入口袋。对我来说,两全其美的方法是从我的手机完全控制一台更大的计算机。
许多新型智能手机都内置了先进的 Web 浏览器。借助这项技术,您可以访问配置为在几乎任何计算机上运行任何命令的界面。在 Linux 机器上运行 Web 服务器非常简单。如果您采取适当的安全措施,您可以快速构建专门为手持设备设计的 Web 界面。
本文展示的方法是使用用户帐户在系统上运行命令。当然,这样做存在安全隐患,但采取适当的预防措施后,可以使其相当安全。
该系统将依赖 Wi-Fi。这在处理手持设备时很有意义,因此请为您的 Wi-Fi 路由器配置密码。想要连接到本地 Intranet 的用户必须先在其设备上输入密码才能看到任何内容。大多数设备都会记住凭据,并在进入范围后自动连接。
为了最大限度地降低安全漏洞事件的风险,我们还应创建权限最小的用户帐户。即使界面仅公开“安全”命令,这也是一个很好的安全措施。
如果尚未安装,请从您的发行版存储库安装以下内容:Apache2、apache2-suexec 和 libapache2-mod-perl2。
第一个软件包是 Web 服务器。如果安装后没有自动启动,请运行命令
/etc/init.d/apache2 start
第二个软件包允许您使用特定系统用户的凭据运行 Web 服务器。安装后,您需要以 root 身份发出以下命令来启用 apache 模块
a2enmod suexec
这里介绍的一些示例需要 Perl CGI 互操作性。最后一个软件包是为此需要的。
现在,您需要配置 Apache 以作为您不太信任的用户运行。在我们的家庭 Linux 机器上,我为所有孩子创建了一个帐户。用户名是“saturn”。此帐户可以执行诸如播放音乐和观看视频之类的操作。但是,它不属于任何可以删除或更改重要内容的组。因此,让我们以这个帐户为例。
编辑您的 apache 配置,并将以下行添加到默认的 VirtualHost (*:80) 或您想要使用的 VirtualHost
SuexecUserGroup saturn saturn
Apache 以 root 身份运行,因此它有能力以任何用户身份运行脚本。上面的行告诉 Apache 以用户 saturn 和组 saturn 身份运行。
现在,使用此命令以 root 身份重启 Apache
/etc/init.d/apache2 restart
Web 服务现在以用户 saturn 身份运行。
从命令行播放声音文件非常简单,并且这是展示此设置的简单性的一个好方法——一个按钮对应一个动作。
我正在使用传统的 Web 技术栈:HTML、CSS、JavaScript 和 CGI。CGI 部分可以使用多种不同的语言来实现。为了简单起见,第一个示例使用 shell 脚本。
在根 Web 目录中创建一个 index.html 文件。对于许多系统,这位于 /var/www/ 中。有些系统使用 /var/www/html/。在此文件中,添加一个调用名为 playQuack() 的 JavaScript 函数的按钮
<button id="quack-button" onclick="playQuack()">Quack</button>
playQuack() 函数的 JavaScript 代码在 bonkers.js 中。整个 index.html 文件如下所示
<html> <head> <title>Bonkers</title> <meta name="viewport" content="initial-scale=1.0; user-scalable=no"/> <link rel="stylesheet" type="text/css" href="default.css" /> <script type="text/javascript" src="bonkers.js"></script> </head> <body> <button id="quack-button" onclick="playQuack()">Quack</button> </body> </html>
一些值得一提的附加内容在元标记中。这告诉智能手机不要缩放页面的内容。如果没有这个,按钮在屏幕上会非常小。
这是我的 default.css 文件。它定义了背景颜色并指定了按钮的外观
html, body { background-color: #1E1E26; } button#quack-button { position: absolute; top: 20%; width: 70%; left: 15%; padding: 5px; border-width: 3px; color: #BFBFBF; font-size: 34px; font-weight: 800; border-color: #9C9C9C; background-image: -webkit-gradient(linear, left top left bottom, from(#BF5A34), to(#463630)); -webkit-border-radius: 10px; }
许多移动浏览器开始支持 WebKit CSS。这令人兴奋,因为几行 WebKit 代码可以做一些非常花哨的事情。最后一个花括号之前的最后两行告诉按钮具有圆角和颜色渐变背景。
现在您有了一个外观漂亮的按钮。将手机上的浏览器指向 Linux 计算机的 IP 地址。您应该会看到类似于图 1 的内容。
接下来,让我们让按钮真正做些事情。在根 Web 目录中创建 bonkers.js 文件,然后输入以下内容
var myDomain = document.domain; var cgiURL = "http://" + myDomain + "/cgi-bin/bonkers.cgi"; var xmlRequest; function playQuack() { xmlRequest = new XMLHttpRequest(); xmlRequest.open("GET", cgiURL, true); xmlRequest.send(null); }
这是构成客户端进程的 JavaScript 代码。它创建一个 URL,该 URL 本质上是在您的 Linux 机器上运行 CGI 脚本。在此示例中,您实际上并不关心 CGI 脚本的返回值。
信不信由你,最难的部分已经完成了。CGI 脚本非常简单易懂——特别是对于习惯使用命令行的人来说。
所有 CGI 脚本都必须位于 cgi-bin 目录中。这通常位于 /var/www/cgi-bin 或 /usr/lib/cgi-bin 中,并且也可以在 Apache 中配置。
这是 CGI 脚本 bonkers.cgi
#!/bin/bash mplayer ~/quack.wav &
就这些。这是一个 Bash shell 脚本。shell 路径的引用在顶部。下面是运行 MPlayer 的命令,它会播放令人讨厌的嘎嘎声。您基本上可以在此处放置任何 shell 命令。
就是这样。任何拥有智能手机和 Wi-Fi 密码的人都可以让计算机发出嘎嘎声。现在是时候做一些更有用的事情了。
我和我的妻子有四个孩子。他们都在争夺我们家 Linux 机器的使用时间。这台名为 Saturn 的计算机始终以帐户“saturn”登录。所有孩子都使用此帐户,并且要么在看电视、视频、YouTube,要么在听音乐,要么在 Internet 上玩 Flash 游戏。我厌倦了在晚餐时间把孩子们从电脑上赶走。这就是创建“big-meanie”界面的原因。
下一个应用程序扩展了前面的示例。我选择使用 Perl 作为 CGI 语言,主要是因为我是一位长期的 Perl 用户和粉丝。在以下示例中,我省略了 HTML 和 CSS 代码,因为它与第一个示例中的代码没有显着差异。
主要区别在于此示例有三个按钮:一个用于启动五分钟倒计时,直到所有乐趣结束。另一个是停止倒计时,以防我改变主意。最后一个是我真的下定决心并且想要立即终止所有适用的程序。
图 2 显示了“大坏蛋”的布局。
第一个按钮给孩子们五分钟倒计时,让他们离开电脑。我认为在桌面上当前窗口上方弹出视觉指示器是合适的。您需要设置一些 shell 变量才能输出到本地显示器:XAUTHORITY、HOME 和 DISPLAY。以下 Perl 命令可以完成此操作
$ENV{'XAUTHORITY'} = '/home/saturn/.Xauthority'; $ENV{'HOME'} = '/home/saturn'; $ENV{'DISPLAY'} = ':0';
这允许窗口在本地显示器上打开,即使该命令是远程发起的。
接下来要注意的是,您有多个功能要执行,因此您需要告知脚本要执行哪个操作(即,按下了哪个按钮)。由于您在此处处理的是 Web URL,因此将您的参数附加到 URL 是实现此目的的最简单方法。使用 ? 符号告诉您的脚本,以下文本是参数。该参数将是要执行的操作。
这是“大坏蛋”的完整 JavaScript 文件
var myDomain = document.domain; // Grab current domain. var cgiURL = "http://" + myDomain + "/cgi-bin/big-meanie.cgi"; var xmlRequest; // This is the logic for the "5 Minute Warning" button function warn5() { xmlRequest = new XMLHttpRequest(); // Add the 5min variable to the URL xmlRequest.open("GET",cgiURL + "?5min", true); xmlRequest.send(null); } // This is the logic for the "Cancel Countdown" button function cancel() { xmlRequest = new XMLHttpRequest(); // Add the cancel variable to the URL xmlRequest.open("GET",cgiURL + "?cancel", true); xmlRequest.send(null); } // This is the logic for the "Get Off Now" button function offnow() { xmlRequest = new XMLHttpRequest(); // Add the off-now variable to the URL xmlRequest.open("GET",cgiURL + "?off-now", true); xmlRequest.send(null); }
每个按钮都有自己的 JavaScript 函数。CGI 脚本的 URL 在每个函数中都附加了相应的变量。然后,该变量通过 xmlRequest.send() 函数传递给 CGI 脚本。
到目前为止,所有操作都发生在手机浏览器上。现在,让我们深入研究服务器上的脚本 big-meanie.pl。
在介绍服务器端脚本(清单 1)之前,还有最后一件事要提及。当发出 URL 请求时,Internet 浏览器期望站点做出响应。如果没有响应,浏览器将挂起。您可以通过创建一个自动以空消息响应的辅助线程来解决此问题。当服务器以这种方式执行命令时,浏览器不会卡住,这对于您接下来要做的事情很重要。
清单 1. 服务器端脚本
#!/usr/bin/perl # Grab the URL variable $variable = $ARGV[0]; # Set some important environment variables. $ENV{'XAUTHORITY'} = '/home/saturn/.Xauthority'; $ENV{'DISPLAY'} = ':0'; $ENV{'HOME'} = '/home/saturn'; # if the 5 minutes warning button is pushed if ($variable =~ /^5min$/) { # Lets create a child process to deal with the notifications defined(my $childpid = fork); if ($childpid) { # If a child pid exists... this is the parent # Send response so the browser is happy print "Content-type: text/html\n\n"; # Show a popup warning message # displaying 5 minutes remaining. `zenity --warning --text='5 minutes left to play'`; } else { # Otherwise it's the child # Print the amount of time left with a subtle gnome # notification message. sleep(60); `notify-send '4 minutes left to play'`; sleep(60); `notify-send '3 minutes left to play'`; sleep(60); `notify-send '2 minutes left to play'`; sleep(60); `notify-send '1 minutes left to play'`; sleep(60); # We are now out of time. # Let's close all the fun applications `/usr/bin/tvtime-command quit`; # Close the TV `pkill mplayer`; # Close mplayer `pkill totem`; # Close Totem movie player `pkill rhythmbox`; # Close the music player `pkill firefox`; # Close the web browser } } # If the Cancel Countdown button has been pushed. if ($variable =~ /^cancel$/) { defined(my $childpid = fork); if ($childpid) { # If parent print "Content-type: text/html\n\n"; } else { `pkill big-meanie`; } } # If the Get Off Now button has been pushed. if ($variable =~ /^off-now$/) { defined(my $childpid = fork); if ($childpid) { # If parent print "Content-type: text/html\n\n"; } else { `/usr/bin/tvtime-command quit`; # Close the TV `pkill mplayer`; # Close mplayer `pkill totem`; # Close Totem movie player `pkill rhythmbox`; # Close the music player `pkill firefox`; # Close the web browser # Just because we can... Send out a tweet that # someone has been kicked off the computer `curl -u <twitterUser>:<twitterPassword> -d status='Someone has been rudely kicked off the computer.' http://twitter.com/statuses/update.xml > /dev/null`; } }
来自 URL 的 action 变量通过 $ARGV[0] 数组获取。后面的 if 语句测试指定了哪个变量,因此,要执行哪个操作。第一个动作,5 分钟, 通过 fork CGI 脚本(创建进程的副本)执行五分钟警告。fork 的“父”分支执行 if 语句的 if 部分,而“子”分支执行 else 部分。父级向浏览器发送一个空的 HTML 响应,以便客户端不会挂起,并使用 Zenity 弹出一个五分钟警告消息框。Zenity 提供了一个简单的 GUI 界面,可以从脚本访问它,并且它应该默认安装在大多数 GNOME 桌面环境中。KDE 用户有替代方案,例如 kdialog 或 whiptail。将弹出对话框放在与倒计时通知分开的线程中的优点是,如果当前用户未单击 Zenity 窗口上的“确定”按钮,脚本不会停顿。
fork 的子部分执行实际的五分钟倒计时。我想在这里表现得人性化一点,并提供关于用户被踢出之前还剩多少时间的细微通知。这允许年龄较大的孩子保存他们的会话或工作。我的小孩子只需要逐渐接受停止的想法。
脚本的子部分在对话框出现后休眠 60 秒。然后,一旦子进程醒来,就会向桌面发送一个简单的通知。它以标准 GNOME 通知的形式出现在屏幕的右上角。图 3 显示了它对用户的显示方式。此过程重复进行,直到倒计时完成。接下来,“大坏蛋”的 meanie 部分开始启动,并向孩子们可能正在享受的任何有趣的程序发送 kill 信号。

图 3. 桌面上显示剩余时间的 GNOME 通知
我使用了 GNOME 常用的 notify-send 命令。它包含在 libnotify-bin 软件包中,并且应该在大多数发行版存储库中。KDE 用户可以使用带有 --passivepopup 标志的 kdialog 来代替 notify-send。
第二个 if 语句用于取消倒计时按钮。它向所有 big-meanie 实例发送 kill 信号,基本上会杀死自身和所有休眠实例,这将杀死任何先前启动的五分钟等待子进程,这些子进程正在等待关闭事物。
最后一个 if 语句不需要太多解释。它立即将用户踢出,并且与第一个语句几乎相同,只是不包括倒计时逻辑。您可能已经注意到代码末尾有趣的小补充:big-meanie 向 Twitter 发送状态更新,声明有人已被踢出计算机。对于 big-meanie 来说,向全世界炫耀某人的乐趣已被终止似乎是合适的。它还可以作为一种安全功能,告知我是否有一个孩子发现了该程序,并且可能以任何不友好的方式在兄弟姐妹身上使用它。
我构建的最常用的应用程序是电视遥控器。我们使用出色的 tvtime 程序来观看所有电视节目。tvtime 软件包包括tvtime-command命令,它允许您控制正在运行的 tvtime 实例的每一个可以想象到的功能。例如,tvtime-command chan-up将频道向上调整一个增量。完整的文档可以在 tvtime 手册页中找到。
我使用了前面描述的所有相同技术,并构建了一个如图 4 所示的完整功能的遥控器。
现在,朋友和家人可以从他们自己的智能手机的舒适性中控制电视。不再有人抱怨使用我们巨大的蓝牙键盘作为遥控器了。孩子们只需拿起最近的 iPod Touch、Palm Pre 或其他任何东西即可。
Jamie Popkin 与他的妻子和四个孩子住在不列颠哥伦比亚省兰茨维尔。他是一名顾问,专门从事 Web 上的地理数据描绘。最近,他开始利用现代 Web/HTML5 技术为智能手机进行开发。可以通过 Twitter (@jamiepopkin) 或电子邮件 (popkinj@littleearth.ca) 与他联系。