我有一个小的 BASH 脚本,我想在几台 Mac 上运行(10.8.5 和 10.9.4 的混合版本)。每当我的 Mac 尝试通过特定的 TCP 端口连接到另一台 Mac 时,我都会触发该脚本。两台计算机的 IP 地址都是已知的,当我通过 nettop 或控制台观看时,我可以看到流量(端口 6472 上的连接)。
我一直没能找到——也许我搜索得不太正确——一个可以在后台运行并监视连接请求,然后在请求发生时运行可执行文件的工具。我想我可以想出一个,但这似乎是别人已经解决的问题。任何现有解决方案的方向都将受到热烈欢迎。
谢谢!Mike
答案1
答案2
另一种替代方案,可能比 @ultrasawblade 解决方案更简单的解决方案可能是运行被敲.——看来有一个适用于 Mac 的版本。
答案3
完成任务最灵活、最个性化的方法可能是仔细阅读dtrace
。
要么使用通用套接字或 tcp 提供程序,并以破坏性模式运行脚本以system()
从内核调用,要么使用函数边界跟踪 (FBT) 作为提供程序。后者将使您的脚本依赖于 OSX 版本。
经过一番尝试,我想到了一个可以为你使用套接字提供程序的最终解决方案提供足够素材的东西(因此它应该可以在你的两台 MacOSX 机器上工作):
#!/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D option switchrate=10hz
#pragma D option destructive
/* AF_INET{6} are unknown to dtrace, so replace with numbers */
inline int af_inet = 2; /* AF_INET */
inline int af_inet6 = 30; /* AF_INET6 */
inline const string procname = "nc";
dtrace:::BEGIN
{
/* Add translations as desired from /usr/include/sys/errno.h */
err[0] = "Success";
err[EINTR] = "Interrupted syscall";
err[EIO] = "I/O error";
err[EACCES] = "Permission denied";
err[ENETDOWN] = "Network is down";
err[ENETUNREACH] = "Network unreachable";
err[ECONNRESET] = "Connection reset";
err[ECONNREFUSED] = "Connection refused";
err[ETIMEDOUT] = "Timed out";
err[EHOSTDOWN] = "Host down";
err[EHOSTUNREACH] = "No route to host";
err[EINPROGRESS] = "In progress";
err[EADDRNOTAVAIL] = "Can't assign requested address";
printf("%-6s %-20s %-8s %-21s %-21s %-8s %s\n",
"PID", "PROCNAME", "FAMILY", "S_ADDR:S_PORT", "D_ADDR:D_PORT",
"LAT(us)", "RESULT");
}
/* MacOSX connectx() syscall:
connectx(arg0:int s, arg1:struct sockaddr *src, arg2:socklen_t srclen,
arg3:struct sockaddr *dsts, arg4:socklen_t dstlen,
arg5:uint32_t ifscope, arg6: associd_t aid,
arg7:connid_t *cid);
*/
syscall::connectx:entry
{
this->s = (struct sockaddr_in *) copyin(arg3, sizeof(struct sockaddr));
this->f = this->s->sin_family;
}
syscall::connectx:entry
/ this->f == af_inet && execname == procname /
{
this->s = (struct sockaddr_in *) copyin(arg1, sizeof(struct sockaddr));
self->address = inet_ntop(this->f, (void *) &this->s->sin_addr);
self->port = ntohs(this->s->sin_port);
self->s_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));
this->d = (struct sockaddr_in *) copyin(arg3, sizeof(struct sockaddr));
self->address = inet_ntop(this->f, (void *) &this->d->sin_addr);
self->port = ntohs(this->d->sin_port);
self->d_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));
self->ts = timestamp;
}
syscall::connectx:entry
/ this->f == af_inet6 && execname == procname /
{
this->s6 = (struct sockaddr_in6 *) copyin(arg1, sizeof(struct sockaddr_in6));
self->port = ntohs(this->s6->sin6_port);
self->address = inet_ntop(this->f, (void *) &this->s6->sin6_addr);
self->s_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));
this->d6 = (struct sockaddr_in6 *) copyin(arg3, sizeof(struct sockaddr_in6));
self->port = ntohs(this->d6->sin6_port);
self->address = inet_ntop(this->f, (void *) &this->d6->sin6_addr);
self->d_addr = strjoin(strjoin(self->address, ":"), lltostr(self->port));
self->ts = timestamp;
}
syscall::connectx:return
/ self->ts /
{
this->delta = (timestamp - self->ts) / 1000;
this->errstr = err[errno] != NULL ? err[errno] : lltostr(errno);
/* Basically anything can be called here */
system("date");
printf("%-6d %-20s %-8d %-21s %-21s %-8d %s\n",
pid, execname, this->f, self->s_addr, self->d_addr,
this->delta, this->errstr);
self->family = 0;
self->ts = 0;
}
将其保存到文件中(假设./socket_connect_mac_simple.d
),然后调用如下代码:
$ sudo ./socket_connect_mac_simple.d
打开另一个终端并启动nc
服务器:
$ nc -4 -k -l 127.0.0.1 6472 > /dev/null
并在另一个终端上连接它:
$ while :; do nc -4 -s 192.168.0.19 127.0.0.1 6472 < /usr/bin/true; sleep 0.5; done
您的输出应该类似于以下内容:
$ sudo ./socket_connect_mac_simple.d
PID PROCNAME FAMILY S_ADDR:S_PORT D_ADDR:D_PORT LAT(us) RESULT
45823 nc 2 192.168.0.19:0 127.0.0.1:6472 240 Success
45825 nc 2 192.168.0.19:0 127.0.0.1:6472 234 Success
45827 nc 2 192.168.0.19:0 127.0.0.1:6472 236 Success
45829 nc 2 192.168.0.19:0 127.0.0.1:6472 240 Success
45831 nc 2 192.168.0.19:0 127.0.0.1:6472 241 Success
45833 nc 2 192.168.0.19:0 127.0.0.1:6472 238 Success
45835 nc 2 192.168.0.19:0 127.0.0.1:6472 234 Success
45837 nc 2 192.168.0.19:0 127.0.0.1:6472 241 Success
45839 nc 2 192.168.0.19:0 127.0.0.1:6472 236 Success
45841 nc 2 192.168.0.19:0 127.0.0.1:6472 237 Success
显然,您将用端口 6472 上的守护进程列表进行替换nc
,并且您还必须调用我放置system("date")
代码片段的 bash 脚本。除此之外,它应该基本上按照您描述的方式工作。
确保您阅读了有关破坏性模式 dtrace 调用的详细文档system()
:DTrace 内部的破坏性模式调用