17

I have written a small 'daemon' in bash that will switch to the headphones if they are detected, and if not, switch to an external USB speaker with PulseAudio.

What I'm looking for is some way to get notification of changes on the file /proc/asound/card0/codec#0, just like inotifywait does on real files (considering files under /proc to be as "pseudo-files").

I find my code a bit insane, because it runs sleep 1 with awk for the whole day, that is 86400 times a day :)

while sleep 1; do
    _1=${_2:-}
    _2=$(awk '/Pin-ctls/{n++;if(n==4)print}' '/proc/asound/card0/codec#0')

    [[ ${_1:-} = $_2 ]] ||
        if [[ $_2 =~ OUT ]]; then
            use_speakers
        else
            use_internal
        fi
done

What I'm looking for is something like (this example doesn't work):

codec=/proc/asound/card0/codec#0
while inotifywait $codec; do
    if [[ $(awk '/Pin-ctls/{n++;if(n==4)print}' $codec) =~ OUT ]]; then
        use_speakers
    else
        use_internal
    fi
done

This way the commands inside the loop would be run only when there are real changes on the $codec file.

admirabilis
  • 4,712
  • 1
    It's not insane -- things like top and GUI system monitors read a whole lot more than that from /proc at short intervals. Of course, they probably do it much more efficiently as compiled executables, but the point is: polling for information is a common task. – goldilocks Sep 14 '13 at 12:54
  • 2
    Since the underlying problem is not unique to you, I would expect there to be some ready-made solution (at least for some hardware) - have a look at http://unix.stackexchange.com/questions/25776/detecting-headphone-connection-disconnection-in-linux and http://superuser.com/questions/339900/how-to-get-a-notification-when-an-audio-jack-is-connected. The ultimate source is of course the kernel tree (and hardware specs if you decide to implement it into some driver). – peterph Sep 14 '13 at 14:42
  • 1
    If this shows up in /proc, you can probably trigger your script with a udev rule, which would be pretty ideal. Less ideal is how tedious it can be coming up with udev rules ;) – goldilocks Sep 14 '13 at 16:31
  • @peterph From what I could gather, hda-verb provides an interface for setting or checking parameters, but it looks like I'll have to run it every second as well. – admirabilis Sep 14 '13 at 16:55
  • @goldilocks Plugging the headphones doesn't send any udev-event. Or is there something more I am missing? – admirabilis Sep 14 '13 at 17:02
  • So you tried udevadm monitor (while plugging the phones in)? As implied it is not my fav thing to wrestle with. I suppose the node in proc might be something that prompts the kernel to check something it can check, and not to report the state of something it has already been informed about. If the former I guess there wouldn't be a previous event. – goldilocks Sep 14 '13 at 17:14
  • @peterph yes, that's a bit weird... – admirabilis Sep 14 '13 at 17:53
  • Files in /proc are generated on demand. There is no generic mechanism for notifying of changes in the reported information. Look in the alsa documentation to see if there's a way to be notified when headphones are plugged in. – Gilles 'SO- stop being evil' Sep 14 '13 at 23:30
  • 1
    @Gilles I did some research, and the information is quite vague. It seems this info is available only in /proc, and that is where the tool mentioned by peterph must get its information from. – admirabilis Sep 15 '13 at 11:18
  • @TAFKA'goldilocks' Don't udev rules (e.g. to "RUN" a script) run as root? It'd be nice to do it as the user, right? – user29020 Apr 17 '14 at 00:01
  • Possible solution to underlying problem: do you have a program called acpi_listen? It prints a message when headphone is plugged/unplugged. If you strace it, you can see it's not polling (i.e. it's not insane); most of the time, it's waiting for data on socket /var/run/acpid.socket. That data is presumably coming from acpid, and if you strace that, you can see it's not insane either; most of the time it's waiting in select(), and what wakes it up is input from /dev/input/input12 on plug/unplug. Non-root can't read that directly, but I guess that's what acpid and acpid_listen are for. – Don Hatch Jun 02 '18 at 12:06
  • Ok, I guess I just repeated what is in the first link suggested by @peterph earlier. With some confirmation that that method is not insane :-) – Don Hatch Jun 02 '18 at 12:16
  • @DonHatch Fortunately, PulseAudio does all of this automatically nowadays! – admirabilis Jun 02 '18 at 16:27
  • inotifywait /proc && can be installed from inotify-tools package – user476926 Jun 10 '21 at 21:42

5 Answers5

14

What I'm looking for is some way to get notification of changes on the file [in proc]

You can't, because they aren't files. This is not quite a duplicate question, but the answer here explains why.

/proc is a kernel interface. There are no real files there, hence they can't change. Reading from the handles is a request and the data in the file when you read it is a reply to that.

The only way you could simulate something like this would be to read the file at intervals and compare the content to see if the reply from the kernel has changed -- looks like you've already done that.

If you stat procfs files, the atime and the mtime will be the same: for some files it is whenever the stat call was, for others a time from during system boot. In the first case, it will always seem to have changed, in the second, it will never seem to have changed.

goldilocks
  • 87,661
  • 30
  • 204
  • 262
  • Unfortunately, even polling it every second adds a considerable latency (eg. 500ms). I hoped there would be a faster/more efficient way of doing this, but since you've mentioned that apps like top do it the same way, I think I'll leave it that way. – admirabilis Sep 14 '13 at 13:38
  • @TeresaeJunior If the latency is an issue (I think it isn't here), e.g., because the duration of the poll is used in a calculation, you would time the actual duration (and not just use the time you asked to sleep). That seems like a lot though; I've never profiled bash scripts so I don't know what would be normal here (hmm...good separate question). Invoking awk == fork() and stuff like that is expensive; utilities written all in C would, as mentioned, have faster methods. I still don't think you are adding much load to the system overall tho. – goldilocks Sep 14 '13 at 13:53
  • 1
    No, sorry, I actually meant: from the time I plug the headphones until the next sleep there is some noticeable delay. But I don't plan on decreasing the sleep time. Thanks for your help! – admirabilis Sep 14 '13 at 16:19
5

If you are using PulseAudio, pactl subscribe does this.

  • Yes, indeed! I started using it after compiling PA 4.0 some months ago due to some audio problems. The version on Debian Stable is 2.0 (although they have uploaded 4.0 to backports recently), and there was no subscribe on 2.0. – admirabilis Apr 26 '14 at 18:14
2

Also take in mind that some files under /proc/ permit to be monitored for changes via polling, for example if you do man proc you can read the following about /proc/self/mounts file:

/proc/[pid]/mounts (since Linux 2.4.19) This file lists all the filesystems currently mounted in the process's mount namespace (see mount_namespaces(7)). The format of this file is documented in fstab(5).

Since kernel version 2.6.15, this file is pollable: after opening the file for reading, a change in this file (i.e., a filesystem mount or unmount) causes select(2) to mark the file descriptor as having an exceptional condition, and poll(2) and epoll_wait(2) mark the file as having a priority event (POLLPRI). (Before Linux 2.6.30, a change in this file was indicated by the file descriptor being marked as readable for select(2), and being marked as having an error condition for poll(2) and epoll_wait(2).)

And that is exactly what is being implemented in the following question:

https://stackoverflow.com/questions/5070801/monitoring-mount-point-changes-via-proc-mounts

Nelson
  • 121
0

I had a similar use-case and found some success monitoring the directory rather than the "file" itself.

Try

inotifywait -r -m /proc/asound/card0/

and you should see events something like:

/proc/asound/card0/ OPEN codec#0
/proc/asound/card0/ ACCESS codec#0
/proc/asound/card0/ CLOSE_NOWRITE,CLOSE codec#0

A simple grep against the codec#0 and a bit of awk will tell you if the file was written to.

AdminBee
  • 22,803
-2

Try to use netlink to monitor /proc files changed.

https://mdlayher.com/blog/linux-netlink-and-go-part-1-netlink/

AdminBee
  • 22,803
meyao
  • 1
  • Welcome to the site. Please add some explanation on how to use netlink to achieve that task; it is not apparent from the external content you linked. Also, it is in general preferred to not have "link-only" answers as the external content may change or be removed (see notice on top of your originally linked page, e.g.), which would dimish the usefulness of you contribution. – AdminBee Feb 17 '20 at 08:54