qmemman_server.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #!/usr/bin/python2
  2. # -*- coding: utf-8 -*-
  3. #
  4. # The Qubes OS Project, http://www.qubes-os.org
  5. #
  6. # Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; either version 2
  11. # of the License, or (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. #
  22. #
  23. import SocketServer
  24. import thread
  25. import time
  26. import xen.lowlevel.xs
  27. import sys
  28. import os
  29. import socket
  30. from qmemman import SystemState
  31. import qmemman_algo
  32. from ConfigParser import SafeConfigParser
  33. from optparse import OptionParser
  34. from qubesutils import parse_size
  35. config_path = '/etc/qubes/qmemman.conf'
  36. SOCK_PATH='/var/run/qubes/qmemman.sock'
  37. LOG_PATH='/var/log/qubes/qmemman.log'
  38. system_state = SystemState()
  39. global_lock = thread.allocate_lock()
  40. def only_in_first_list(l1, l2):
  41. ret=[]
  42. for i in l1:
  43. if not i in l2:
  44. ret.append(i)
  45. return ret
  46. def get_domain_meminfo_key(domain_id):
  47. return '/local/domain/'+domain_id+'/memory/meminfo'
  48. class WatchType:
  49. def __init__(self, fn, param):
  50. self.fn = fn
  51. self.param = param
  52. class XS_Watcher:
  53. def __init__(self):
  54. self.handle = xen.lowlevel.xs.xs()
  55. self.handle.watch('@introduceDomain', WatchType(XS_Watcher.domain_list_changed, None))
  56. self.handle.watch('@releaseDomain', WatchType(XS_Watcher.domain_list_changed, None))
  57. self.watch_token_dict = {}
  58. def domain_list_changed(self, param):
  59. curr = self.handle.ls('', '/local/domain')
  60. if curr == None:
  61. return
  62. global_lock.acquire()
  63. for i in only_in_first_list(curr, self.watch_token_dict.keys()):
  64. #new domain has been created
  65. watch = WatchType(XS_Watcher.meminfo_changed, i)
  66. self.watch_token_dict[i] = watch
  67. self.handle.watch(get_domain_meminfo_key(i), watch)
  68. system_state.add_domain(i)
  69. for i in only_in_first_list(self.watch_token_dict.keys(), curr):
  70. #domain destroyed
  71. self.handle.unwatch(get_domain_meminfo_key(i), self.watch_token_dict[i])
  72. self.watch_token_dict.pop(i)
  73. system_state.del_domain(i)
  74. global_lock.release()
  75. system_state.do_balance()
  76. def meminfo_changed(self, domain_id):
  77. untrusted_meminfo_key = self.handle.read('', get_domain_meminfo_key(domain_id))
  78. if untrusted_meminfo_key == None or untrusted_meminfo_key == '':
  79. return
  80. global_lock.acquire()
  81. system_state.refresh_meminfo(domain_id, untrusted_meminfo_key)
  82. global_lock.release()
  83. def watch_loop(self):
  84. # sys.stderr = file('/var/log/qubes/qfileexchgd.errors', 'a')
  85. while True:
  86. result = self.handle.read_watch()
  87. token = result[1]
  88. token.fn(self, token.param)
  89. class QMemmanReqHandler(SocketServer.BaseRequestHandler):
  90. """
  91. The RequestHandler class for our server.
  92. It is instantiated once per connection to the server, and must
  93. override the handle() method to implement communication to the
  94. client.
  95. """
  96. def handle(self):
  97. got_lock = False
  98. # self.request is the TCP socket connected to the client
  99. while True:
  100. self.data = self.request.recv(1024).strip()
  101. if len(self.data) == 0:
  102. print 'EOF'
  103. if got_lock:
  104. global_lock.release()
  105. return
  106. if got_lock:
  107. print 'Second request over qmemman.sock ?'
  108. return
  109. global_lock.acquire()
  110. got_lock = True
  111. if system_state.do_balloon(int(self.data)):
  112. resp = "OK\n"
  113. else:
  114. resp = "FAIL\n"
  115. self.request.send(resp)
  116. def start_server(server):
  117. server.serve_forever()
  118. class QMemmanServer:
  119. @staticmethod
  120. def main():
  121. usage = "usage: %prog [options]"
  122. parser = OptionParser(usage)
  123. parser.add_option("-c", "--config", action="store", dest="config", default=config_path)
  124. (options, args) = parser.parse_args()
  125. logfd = os.open(LOG_PATH, os.O_WRONLY|os.O_APPEND|os.O_CREAT, 0644)
  126. if logfd < 0:
  127. print sys.stderr, "ERROR: Failed to open log file (%s)" % LOG_PATH
  128. exit(1)
  129. # reinitialize python stdout/err
  130. sys.stdout.flush()
  131. sys.stderr.flush()
  132. os.dup2(logfd, 1)
  133. os.dup2(logfd, 2)
  134. os.close(logfd)
  135. devnull = os.open('/dev/null', os.O_RDONLY)
  136. os.dup2(devnull, 0)
  137. config = SafeConfigParser({
  138. 'vm-min-mem': str(qmemman_algo.MIN_PREFMEM),
  139. 'dom0-mem-boost': str(qmemman_algo.DOM0_MEM_BOOST),
  140. 'cache-margin-factor': str(qmemman_algo.CACHE_FACTOR)
  141. })
  142. config.read(options.config)
  143. if config.has_section('global'):
  144. qmemman_algo.MIN_PREFMEM = parse_size(config.get('global', 'vm-min-mem'))
  145. qmemman_algo.DOM0_MEM_BOOST = parse_size(config.get('global', 'dom0-mem-boost'))
  146. qmemman_algo.CACHE_FACTOR = config.getfloat('global', 'cache-margin-factor')
  147. print "values: %s, %s, %s" % (str(qmemman_algo.MIN_PREFMEM), str(qmemman_algo.DOM0_MEM_BOOST), str(qmemman_algo.CACHE_FACTOR))
  148. try:
  149. os.unlink(SOCK_PATH)
  150. except:
  151. pass
  152. os.umask(0)
  153. server = SocketServer.UnixStreamServer(SOCK_PATH, QMemmanReqHandler)
  154. os.umask(077)
  155. # notify systemd
  156. nofity_socket = os.getenv('NOTIFY_SOCKET')
  157. if nofity_socket:
  158. s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
  159. if nofity_socket.startswith('@'):
  160. nofity_socket = '\0%s' % nofity_socket[1:]
  161. s.connect(nofity_socket)
  162. s.sendall("READY=1")
  163. s.close()
  164. thread.start_new_thread(start_server, tuple([server]))
  165. XS_Watcher().watch_loop()