1

I'm working on a 'local' PHP project. It's a PHP app running in a Debian VM intended for use on the user's own PC.

I have a couple of python scripts running as root services from /etc/init.d which handle the apps updates, logs, communication to USB peripherals and misc stuff.

EDIT 1: The VM is a Turnkey Linux LAMP image, and python version is 2.7.3, MySQL 5.5.47

The PHP app talks to the python services via TCP sockets, sends requests for the specific tasks the user needs.

Now, I'm trying to let the user choose between having the MySQL server only bind to localhost or share it with other users on the network. For this I have the python service go through the my.cnf file, and comment out the bind-address = 127.0.0.1 line, or uncomment it back to make the server local again.

After the my.cnf edit, the python script calls /etc/init.d/mysql restart to have the changes take effect.

The thing is, now mysql ends up listening on the port the python service was listening on for the PHP requests, and if I need to restart the service Python fails, claiming it can't open the port because it's already in use.

I can't figure out why the hell would mysql end up listening on the port the python service was using instead of 3306 as the my.cnf file says.

Also if I ssh onto the VM and manually restart mysql it goes back to listening on 3306 and I can start the python service OK.

Some code on the python side:

RECV_BUFFER = 4096
DAEMON_PORT = 5555

daemon_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
daemon_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
daemon_socket.bind(("localhost", DAEMON_PORT))
daemon_socket.listen(10)

//// SOCKET HANDLING, QUEUE and etc

if (data =='share:enable'):
    import fileinput
    import re
    import subprocess

    for line in fileinput.input('/etc/mysql/my.cnf', inplace = 1): 
        print re.sub(r'#*bind-address',"#bind-address", line),

    print subprocess.check_output(['/etc/init.d/mysql','restart'])

Sample output:

$> lsof -i
...
...
python    17219        root    4u  IPv4  25311      0t0  TCP localhost:5555 (LISTEN)   
mysqld    20773       mysql   10u  IPv4  33299      0t0  TCP localhost:mysql (LISTEN)
$> echo 'share:enable' | netcat localhost 5555
$> lsof -i
....
....
python    17219        root    4u  IPv4  25311      0t0  TCP localhost:5555 (LISTEN)                                           
mysqld_sa 17361        root    4u  IPv4  25311      0t0  TCP localhost:5555 (LISTEN)                                           
mysqld    17856       mysql    4u  IPv4  25311      0t0  TCP localhost:5555 (LISTEN)                                           
mysqld    17856       mysql   13u  IPv4  25782      0t0  TCP localhost:mysql (LISTEN)                                          
logger    17857        root    4u  IPv4  25311      0t0  TCP localhost:5555 (LISTEN) 

Edit 2: Added my my.cnf MySQL config file.

#                                                                                                                                                                              
# The MySQL database server configuration file.
#                                                                                                                                                                              
# You can copy this to one of: 
# - "/etc/mysql/my.cnf" to set global options, 
# - "~/.my.cnf" to set user-specific options.
#                                                                                                                                                                              
# One can use all long options that the program supports. 
# Run program with --help to get a list of available options and with 
# --print-defaults to see which it would actually understand and use.
#                                                                                                                                                                              
# For explanations see 
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# This will be passed to all mysql clients 
# It has been reported that passwords should be enclosed with ticks/quotes 
# escpecially if they contain "#" chars... 
# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
[client]
port = 3306 
socket = /var/run/mysqld/mysqld.sock

# Here is entries for some specific programs 
# The following values assume you have at least 32M ram

# This was formally known as [safe_mysqld]. Both versions are currently parsed.
[mysqld_safe] 
socket = /var/run/mysqld/mysqld.sock 
nice = 0

[mysqld]
#                                                                                                                                                                              
# * Basic Settings
#                                                                                                                                                                              
user = mysql 
pid-file = /var/run/mysqld/mysqld.pid 
socket = /var/run/mysqld/mysqld.sock 
port = 3306 
basedir = /usr 
datadir = /var/lib/mysql 
tmpdir = /tmp 
lc-messages-dir = /usr/share/mysql 
skip-external-locking
#                                                                                                                                                                              
# Instead of skip-networking the default is now to listen only on 
# localhost which is more compatible and is not less secure.
bind-address = 127.0.0.1
#                                                                                                                                                                              
# * Fine Tuning
#                                                                                                                                                                              
key_buffer = 16M 
max_allowed_packet = 16M 
thread_stack = 192K 
thread_cache_size = 8
# This replaces the startup script and checks MyISAM tables if needed 
# the first time they are touched
myisam-recover = BACKUP
#max_connections = 100 
#table_cache = 64 
#thread_concurrency = 10
#                                                                                                                                                                              
# * Query Cache Configuration
#                                                                                                                                                                              
query_cache_limit = 1M 
query_cache_size = 16M
#                                                                                                                                                                              
# * Logging and Replication
#                                                                                                                                                                              
# Both location gets rotated by the cronjob. 
# Be aware that this log type is a performance killer. 
# As of 5.1 you can enable the log at runtime! 
#general_log_file = /var/log/mysql/mysql.log 
#general_log = 1
#                                                                                                                                                                              
# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
#                                                                                                                                                                              
# Here you can see queries with especially long duration 
#log_slow_queries = /var/log/mysql/mysql-slow.log 
#long_query_time = 2 
#log-queries-not-using-indexes
#                                                                                                                                                                              
# The following can be used as easy to replay backup logs or for replication. 
# note: if you are setting up a replication slave, see README.Debian about
#       other settings you may need to change. 
#server-id = 1 
#log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10 
max_binlog_size = 100M
#binlog_do_db = include_database_name 
#binlog_ignore_db = include_database_name
#                                                                                                                                                                              
# * InnoDB
#                                                                                                                                                                              
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. 
# Read the manual for more InnoDB related options. There are many!
#                                                                                                                                                                              
# * Security Features
#                                                                                                                                                                              
# Read the manual, too, if you want chroot! 
# chroot = /var/lib/mysql/
#                                                                                                                                                                              
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
#                                                                                                                                                                              
# ssl-ca=/etc/mysql/cacert.pem 
# ssl-cert=/etc/mysql/server-cert.pem 
# ssl-key=/etc/mysql/server-key.pem



[mysqldump] 
#quick quote-names 
max_allowed_packet = 16M

[mysql]
#no-auto-rehash # faster start of mysql but no tab completition

[isamchk] 
key_buffer = 16M

#                                                                                                                                                                              
# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#                                                                                                                                                                              
!includedir /etc/mysql/conf.d/
  • 1
    Hello, may we have a copy of you my.cnf network configuration ? Also, can you confirm that your Python script runs with the root user ? – DeadEye Feb 9 '16 at 16:02
  • Mysql listens to wrong port on restart Why did you conclude this? – 030 Feb 9 '16 at 23:14
  • I agree with @DeadEye. The MySQL config is required to answer the question. – 030 Feb 9 '16 at 23:15
  • @DeadEye Why should it run as root? It depends whether that is required. How did you come up with that conclusion? – 030 Feb 9 '16 at 23:16
  • @Alfred I can see after the restart an mysqld and an mysql_sa process listening on port 5555,and that's my python code's port, didn't configure it for MySQL anywhere – jcane86 Feb 9 '16 at 23:19
6
+50

This is less to do with mysql and more to do with python.

When you start a process, it inherits a lot of information from the process that created it, including its file descriptors. What your finding is that mysql inherits the listening socket your process created when it spawned.

To resolve this, you need to change your python code to not use subprocess.check_output(...) and instead use subprocess.Popen(..., close_fds=True)

Alternatively, setting the CLOEXEC flag on your socket will prevent it ever being inherited at all -- albeit you need to test this behaviour works throughout all your code paths and nothing strange occurs because of it. The following is the basis for doing this.

from fcntl import fcntl, FD_CLOEXEC, F_GETFD, F_SETFD

flags = fcntl(socketfd, F_GETFD)
flags |= FD_CLOEXEC
fcntl(socketfd, F_SETFD, flags)

Either will resolve the listening socket issue you have.

| improve this answer | |
  • That's awesome, I was looking for something like that.. Just that I interpreted that it was using it as the server port, but it's really just keeping it because it got it from its' parent process, right? I'll test it as soon as I get back to my computer, thanks a lot! – jcane86 Feb 11 '16 at 21:23
  • @jcane86 I updated with an additional solution. – Matthew Ife Feb 11 '16 at 21:24
  • Ohh, you rock! totally spot on... the first solution works like a charm... for bonus points, how would I go about keeping the output print? I tried subprocess.popen(...).communicate()[0] but it prints None – jcane86 Feb 11 '16 at 22:01
  • I'd probably go with making the socket have the FD_CLOEXEC flag in solution 2, given it prevents you tripping up over the problem again and lets you use check_output(). – Matthew Ife Feb 11 '16 at 22:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.