Open mennis opened 6 years ago
I can reproduce your behavior with:
I've looked at this a bit and I don't think osxfuse is to blame.
Acme creates a new window in response to Twalk of the new
directory (see $PLAN9/src/cmd/acme/fsys.c:/newwindowthread
), you can observe this using 9p ls acme/new
, which creates two new windows on my system without me having mounted acme.
Using 9pfuse -D $(namespace)/acme /mnt/acme
(which is basically what acme does with the -m
flag, except the -D
flag which logs all FUSE and 9P messages) we can check where the accesses to new
originate.
A sequence of related messages looks like this:
FUSE -> len 4 unique 0x3 uid 501 gid 20 pid 404 Lookup nodeid 0x1 name 'new'
<- Twalk tag 0 fid 0 newfid 10 nwname 1 0:new
-> Rwalk tag 0 nwqid 1 0:(0000000000001600 0 d)
This tells us that the process with PID 404 (Finder.app), started by the user with UID 501 (myself) sent a Lookup message for new
, which 9pfuse translated into a Twalk request for new
. (Rwalk is the corresponding response.)
Letting it run for a while I can see messages like this from Finder and from fseventsd.
I'm not completely sure why these programs are doing this (fseventsd is presumably scanning for changes, Finder may be looking for display information like a volume icon), but I don’t think osxfuse can prevent this (the nobrowse
mount option doesn't seem to help).
The only way I can think to work around this is by not opening a new window in response to Twalk but when opening a file inside new
, the way the manpage describes it.
As far as I understand this should be compatible to both what acme(4) says and to the way lib/acme.rc operates, but I haven't tested it yet.
What fixed this for me was disabling "Show icon preview" in Finder's view options.
Not solving it for me on either Fuse 3.7.1 and 3.8.0
Has any progress been made? I'm also using Acme via plan9ports on macOS 10.11.6, but mounting it results in the same stream of blank windows opening constantly in the program.
I've looked at the source file @mkhl mentioned.
if(strcmp(x->fcall.wname[i], "new") == 0){
if(w)
error("w set in walk to new");
sendp(cnewwindow, nil); /* signal newwindowthread */
w = recvp(cnewwindow); /* receive new window */
incref(&w->ref);
type = QTDIR;
path = QID(w->id, Qdir);
id = w->id;
dir = dirtabw;
goto Accept;
}
I was contemplating replacing the body of this block with:
if(strcmp(x->fcall.wname[i], "new") == 0) {
if(w) error("w set in walk to new");
continue;
}
But I'm hesitant to screw up anything I might not understand. Has anyone found a way to avoid this without editing the source code?
I was contemplating replacing the body of this block with:
I believe that would prevent scripts from opening windows by reading from new.
For example, acme.rc
provides this:
fn newwindow {
winctl=`{9p read acme/new/ctl}
winid=$winctl(1)
}
Has anyone found a way to avoid this without editing the source code?
I haven’t.
So the actual mechanic for opening windows in Acme is to read acme/new/ctl
, and then grab the returned window identifier so you can manipulate the associated tag files and whatnot? This means there is essentially no distinction between my filesystem daemon meandering through the mounted file system and an intentional read to create a new window?
What can realistically be done here? Is there any way to get the file system daemon to ignore the mounted filesystem, or finder for that matter?
So the actual mechanic for opening windows in Acme is to read
acme/new/ctl
, and then grab the returned window identifier so you can manipulate the associated tag files and whatnot?
The actual mechanism is doing anything inside acme/new
. Writing to acme/new/body
works too, for example. This way has the advantage of giving you the winid
though so you can continue manipulating the window.
Is there any way to get the file system daemon to ignore the mounted filesystem, or finder for that matter?
I hope there’s a way to do that, but I haven’t been able to dig in beyond what I wrote above. One could try to figure out what exactly Finder and fseventsd do and if/how one can tell them not to. It’s also possible that there are (or should be) mount options that we could use, so figuring that out might make sense.
It’s also possible that there are (or should be) mount options that we could use, so figuring that out might make sense.
Perhaps FUSE can be instructed to make the mounted filesystem only visible to a certain process group. I'd have to look at how Acme executes other programs, but if all it does is fork and exec then maybe that would work.
Other programs can interact with the filesystem by using 9P libraries or 9p
if they can shell out.
The mounts are for programs that cannot do either or where an actual filesystem has benefits over pipes.
Using programs that interact with Acme from outside of Acme is quite common.
Okay. This morning I took another look at how FUSE is integrated with Acme, and decided I'd have it ignore any Lookup's that come from Finder or fseventsd. By ignore, I mean it actually reports there is no such file or directory to them.
I added the following segment in $PLAN9/src/cmd/9pfuse/main.c
above fuse lookup
.
/* List of processes you don't want browsing the mounted filesystem. */
const char *procBlacklist[] = {
"fseventsd",
"Finder",
NULL
};
/* Returns nonzero if the given string is contained in the list. */
int containsString (const char *s, const char **ss) {
while (*ss != NULL && strcmp(*ss, s) != 0) {
ss++;
}
return (*ss != NULL);
}
/* Segments string with delimiter character 'd'. Returns last segment. */
const char *segmentedStringSuffix (const char *s, char d) {
char *p;
// If input is NULL, return NULL. If no delimiter exists, return last segment.
if (s == NULL || (p = strchr(s, d)) == NULL) {
return s;
}
// Find last segment and return it.
do {
s = p + 1;
} while ((p = strchr(s, d)) != NULL);
return s;
}
/* Returns the command of a process given it's PID. */
const char *getProcComm (pid_t pid) {
int child, fds[2];
size_t len;
static char buf[1024];
char pid_buf[6];
// Prepare pid argument buffer.
snprintf(pid_buf, 6, "%" PRIdMAX, (intmax_t)pid);
// Create pipe.
if (pipe(fds) == -1) {
fprint(3, "getProcComm: Pipe failed!");
}
// Fork. If Child: redirect output to pipe and execute ps.
if ((child = fork()) == 0) {
close(fds[0]);
close(STDOUT_FILENO);
dup(fds[1], -1);
execlp("ps", "-p", pid_buf , "-o", "comm=", NULL);
} else {
close(fds[1]);
}
// Read result into buffer. Error out if zero.
len = read(fds[0], buf, 1024);
// Append null character on newline.
buf[len - 1] = '\0';
// Wait on child.
wait();
return buf;
}
/* Returns nonzero if the given PID is blacklisted. */
int blacklisted (pid_t pid) {
const char *command = getProcComm(pid);
return containsString(segmentedStringSuffix(command, '/'), procBlacklist);
}
I also had to include:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
The changed file is also linked here.
Edit: I forgot the most important part, how I modified the lookup function:
void
fuselookup(FuseMsg *m)
{
char *name;
Fusefid *ff;
CFid *fid, *newfid;
Dir *d;
struct fuse_entry_out out;
/* Ignore Lookup's by blacklisted PIDs. */
if (blacklisted(m->hdr->pid)) {
replyfuseerrno(m, ENOENT);
return;
}
...
}
Now, I don't know what secondary effects this will have, but I've managed to stop the trickle of windows appearing in Acme, while still being able to explicitly trigger them myself. I must say I find it hard to understand exactly what is going on in these files. They are sparsely commented and written in a style that is unfamiliar to me. Hopefully someone with a much better knowledge of this can help in finding an elegant solution (if mine even works).
I'll update this thread as I find out more.
Has this worked out for you well?
@ilanpillemer Yeah, it solved it for me. It's just an "ugly" workaround and I wouldn't consider it appropriate for merging with the actual source code. But if you make the changes I did and recompile Acme then it should no longer spawn windows.
Thanks.... that window spawning is maddening.
No problem. Hopefully it works for you too.
I think this might go away if you use a directory in /tmp for your mount point. Also I've opened a TSI with apple to see it there is an official way to do this. I'll post their response if it contains information.
@mkhl Where are you attempting to insert the no browse directive? I'd like to test that.
@mennis I inserted the option in the appropriate call to /usr/local/plan9/src/cmd/9pfuse/fuse.c:/execl/
(the first one on my system).
The available options are documented on the osxfuse wiki and summarised when calling mount_osxfuse
without arguments.
I got a response from Apple. I was hoping to fix this myself but haven't and it's been a while since they responded. Essentially I was told that if one wants to insure that the volume is ignored by fseventsd it needs to be mounted outside of /User with "MNT_DONTBROWSE” set on it. In addition calling statfs inside acme the file system should not illicit action in the way that writing to acme/new does.
where would the changes need to be made?
On Thu, 10 Jan 2019 at 17:56 mennis notifications@github.com wrote:
I got a response from Apple. I was hoping to fix this myself but haven't and it's been a while since they responded. Essentially I was told that if one wants to insure that the volume is ignored by fseventsd it needs to be mounted outside of /User with "MNT_DONTBROWSE” set on it. In addition calling statfs inside acme the file system should not illicit action in the way that writing to acme/new does.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/9fans/plan9port/issues/136#issuecomment-453191974, or mute the thread https://github.com/notifications/unsubscribe-auth/AA2cjmrG_MXYv8zYCt44xlKszVz01sH8ks5vB37ggaJpZM4RxfLk .
Might be a beta issue. Running Fuse 3.7.1
167 0 0xffffff7f8330e000 0x19000 0x19000 com.github.osxfuse.filesystems.osxfuse (3.7.1) 8C89BD7B-DA6D-347D-AC76-759C45AE6099 <7 5 4 3 1>
and OSX 10.13.4 Beta (17E139j) when I run acme without -m /mnt/acme it behaves normally. When using acme with this option the acme's looks normal when exploring it from the command line but periodically an empty buffer with no path appears in acme.