Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIGTSTP keeps the speaker active #31

Open
deoxal opened this issue Sep 12, 2022 · 17 comments · May be fixed by #35
Open

SIGTSTP keeps the speaker active #31

deoxal opened this issue Sep 12, 2022 · 17 comments · May be fixed by #35
Assignees

Comments

@deoxal
Copy link

deoxal commented Sep 12, 2022

I would think that SIGTSTP shown as ^Z should pause a long tone from playing and start playing again when fg is entered. And if not that then just halt the process the same as SIGINT - ^C.

Or is there a reason that it's this way that I am missing?

I ask because I just pressed Ctrl + Z by accident and couldn't stop the speaker. I had to run sudo rmmod pcspkr so I wouldn't have to listen to it while I looked up how to reverse a SIGSTP for if I did that again.

@ndim
Copy link
Member

ndim commented Sep 12, 2022

Thank you very much for the report! This is awesome. Such an old program, and the first time this comes up is in 2022.

First, to turn off the speaker, you can just run beep again. Running beep turns the speaker on, waits a bit, and then turns the speaker off, and the latter bit is what you were looking for.

Second, as to what the "proper" behaviour should be for Ctrl+Z and fg: I do not know yet. I will have to reflect on that a bit more, with possibly some input from other people here.

Again, thank you!

@ndim ndim self-assigned this Sep 12, 2022
@deoxal
Copy link
Author

deoxal commented Sep 13, 2022

Ya no problem.

Is there a newer preferred way to control the PC speaker then? When I looked up how to control the speaker, beep was one of the top results.

When I installed it, I was at a loss as to why it didn't just play the tone as a non root user, so I tried with sudo and it said it wouldn't run as root for security reasons. This led me to find I had to add a udev rule and presto it works but I don't understand why the package manager doesn't create the udev rule when installing the program. I also saw that some distros are disabling the pcspkr module which will make it harder for new users if they find a guide that doesn't say they need to run sudo modprobe pcspkr.

@ndim
Copy link
Member

ndim commented Sep 13, 2022

One possible behaviour for when beep is interrupted by ^Z: If beep has started a tone, it sets a flag and stops the tone before suspending. Upon resume, if the flag is set, beep could then restart the tone with the last frequency before waiting for the remainder of the tone length.

I will address your other remarks and questions later.

@ndim
Copy link
Member

ndim commented Sep 13, 2022

How to control the speaker: Well, the PC speaker has been on its way out for the last 20 years. Laptops have a proper sound system to which the PC speaker output may or may not be routed, and desktop PCs often come without the speaker or buzzer connected to the connectors on the PC motherboard. For many years, notification noises usually go through the PCM sound output (think error notification dialogs popping up, or terminals beeping on backspace in an empty line).

So the industry appears to have stopped caring for the PC speaker, except maybe for pre-boot or firmware early boot problems which can be signalled by a speaker connected to the motherboard.

So not much work has been put into software support either. Linux has its API (turn it on with frequency X, turn it off) in two reincarnations (I call them console and evdev), FreeBSD has its API (play frequency X for duration Y, so the kernel maintains some state), and I have no idea about API on Windows PCs or about hardware support and API on Macs.

On Linux, beep is the only program I am aware of for controlling the class PC speaker. On the other hand, many programs exist which produce audible signals via PCM sound, both for synthesizing sounds in realtime and for just playing a prepared PCM audio file.

As to how to install beep... as maintainer of the beep software package I have tried to make the beep software well documented. However, I cannot deal with very system specific thing programmatically in this package, so some system specific steps will always be necessary after installing beep with make install.

If you need to build and install beep from source, I have tried to document everything you need to know from the beep package's side, but you need to know how to adapt it to your system.

If you use a Linux distribution's beep package instead of installing beep from source, a well made distro package should mainly make beep work out of the box.

However, even the Fedora beep package which I happen to maintain myself cannot just work out of the box: Most systems these days never use the PC speaker, so the system by default not even installing the pcspkr.ko module is a reasonable default. So after installing the beep packge, the system administrator needs to also install the package with the extra modules (limitations in the package tools disallow a package dependency) and then load the pcspkr.ko kernel module for the currently running system and configure the system to load pcspkr.ko after avery future reboot. And while the Fedora beep package's default permission setup should make beep just work out the box for most to all situations when a user is logged in locally, a non-root ssh session still means the sysadmin needs to add that non-root user to the beep group - and there is no good way around this either.

For some time, the Debian package decided to not set up the beep group, and so the sysadmin also had to add the beep group, then set up the udev script to allow the beep group access. I did not understand that decision back then, but AFAIK the Debian beep package also ships the beep group and udev scripts now.

So different distributions' beep packages will do different things here.

If someone has an idea to simplify this mess, both on the beep software package level as on a beep distro package and linux distribution level, I am all ears. However, the PC speaker is on the way out and has been on the way out for a long time, so I would not expect linux distros to be very enthusiastic about doing big changes to their distribution for PC speaker support.

@ndim
Copy link
Member

ndim commented Sep 13, 2022

So it appears there is SIGSTOP and SIGTSTP which stop a process, and SIGCONT lets them continue. It appears I can install handlers for all three, but SIGSTOP is a signal which cannot be ignored.

So both SIGSTOP and SIGTSTP should silence the PC speaker, and SIGCONT should re-enable the PC speaker. The state machine for that must handle repeated signals, e.g. many SIGCONT signals arriving without stop signal in between.

I need to do some experiments with code there, and some hard thinking about security and robustness, the latter maybe including some outside expertise.

And the whole thing might be easier if beep only supported the evdev API.

@deoxal
Copy link
Author

deoxal commented Sep 18, 2022

Wow thanks for the detailed response and maintaining this program.

@deoxal
Copy link
Author

deoxal commented Sep 19, 2022

Where do I have to look to keep programs from using beep? I only want it to use it in my scripts and to play with but Firefox makes it beep when using the "Find in page" bar and xed uses it when pushing the arrow keys or backspace when the cursor cannot be moved but there but I can't find a setting to disable it in either.

Edit: xset -b off disabled it

I don't know of a way to reenable that once it is disabled though.

@ndim
Copy link
Member

ndim commented Sep 20, 2022

Desktop programs like firefox or gnome-terminal do not use the beep program.

They use some desktop environment interface, and that eventually generates something audible, and that audible thing is very probably not the PC speaker hardware (which beep interfaces to), but it could be. That audible thing is usually PCM audio which you can set up somewhere in your application or your desktop environment's settings with some WAV files, and also disable at all.

The details vary depending on your application and desktop environment.

@ndim
Copy link
Member

ndim commented Sep 20, 2022

In my desktop environment (Gnome 43 RC on Fedora 37 Beta), xset has no effect. I kind of remember that xset did have an effect on my X11/Xorg systems related to the PC speaker 10 to 20 years ago.

Anyway, xset --help suggests there is xset -b on going along with xset -b off.

@deoxal
Copy link
Author

deoxal commented Sep 20, 2022

It is the buzzer, it happens while my speakers are off and not until after I added the evdev rule.

I tried, xset -b on, afterwards but it didn't reactivate it.

Edit: it's xset -b or xset b off to turn off but only xset b on to turn on.

@ndim
Copy link
Member

ndim commented Sep 28, 2022

A quick stop gap measure could be installing the following noop signal handler for SIGTSTP after the other calls to signal(2):

    signal(SIGTSTP, handle_signal_noop);

However, this changes the program behaviour not only regarding whether the sound continues while the beep process is stopped, but also regarding the timing:

  • Without the TSTP handler added, a long-running beep process on ^Z will interrupt its current nanosleep() waiting period without changing the speaker hardware, and on resume (shell command fg) it will continue the interrupted nanosleep() waiting period.
  • With a noop TSTP handler added, a long-running beep process on ^Z will interrupt its current nanosleep() waiting period by having the nanosleep() call return and then the remaining code shutting off the speaker hardware and potentially starting the next tone before the beep process is actually put in the background, but on resume (shell command fg) the beep process will continue playing the next tones.

I fear to implement this properly, the main loop must be completely rewritten from scratch as a proper event loop running a state machine which can react to the proper signals and other events, especially on SIGTSTP turning off the speaker and after SIGCONT turning the speaker back on and continue the interrupted waiting period.

sigaction() might help a bit with its more detailed options than signal() offers.

@ndim
Copy link
Member

ndim commented Sep 30, 2022

It appears that beep cannot use signalfd(2) and then neatly fold all the signals into a select(2)/poll(2)/epoll_wait(2) based event loop, as neither of those functions appears to allow finding out the time remaining after they have been interrupted by a signal.

So nanosleep(2) gives us the time remaining, and we apparently need to catch the signals using signal handlers. This makes the code more complex, but I do not see a way around this (yet).

@deoxal
Copy link
Author

deoxal commented Oct 1, 2022

Why can't the time in nanoseconds be read from the real time clock when beep is run and then read again in the event loop? That plus the -l parameter would be everything needed to find the remaining time.

@ndim
Copy link
Member

ndim commented Oct 1, 2022

Hmm. Maybe. Of course there are event loop libraries which offer timer sources, but I do not know how they do it and I also do not want beep to have essential (and possibly big) build dependencies beyond a C compiler, a libc, and a Linux kernel.

@deoxal
Copy link
Author

deoxal commented Oct 1, 2022

Well you can always statically link. How large are these libraries?

@ndim
Copy link
Member

ndim commented Oct 1, 2022

Some libraries implementing event loops for C code which I could find quickly:

  • sd_event - requires systemd-devel
  • glib - brings part of the Gtk universe of tools and libraries
  • libev - using cvs for development does not inspire confidence
  • libevent
  • libuv

Anyway, I do not want to link against any of these. At most, I want to find out from them how to best implement a timer in a way suitable for beep.

@ndim
Copy link
Member

ndim commented Oct 2, 2022

Quick note for later: timerfd_create(2)

Hooking timerfd_create(2) into epoll together with signalfd(2) and possibly STDIN_FILENO (if -s or -c are given on the command line) should result in a nice simple event loop.

@ndim ndim linked a pull request Oct 3, 2022 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants