class Jamf::DBConnection

A mysql connection to the JSS database.

This is a singleton class, only one can exist at a time, and it is created, but not connected, automatically when the module loads.

Use it via the Jamf::DB_CNX constant (for connection metadata) and the Jamf::DB_CNX.db attribute (which contains the actual mysql query interface) for making queries

Direct MySQL access is minimal and discouraged, since it bypasses the API, and can be very dangerous. However, it’s necessary to overcome some limitations of the API or to access custom tables.

While a database connction isn’t required for most things, warnings will be sent to stderr when functionality is limited due to a lack of a database connection i.e. when Jamf::DB_CNX.connected? == false

To make a connection with credentials, just call the connect method thus:

Jamf::DB_CNX.connect :server => 'server.company.com', :user => "user", :pw => "pw"

Other options include:

:db_name => which database to connect to, defaults to 'jamfsoftware'
:port => tcp port for connection to server, defaults to the standard mysql port.
:connect_timeout => seconds to wait before giving up on connection, defaults to 120
:read_timeout => seconds to wait before giving up on recieving data, defaults to 120
:write_timeout => seconds to wait before giving up on sending data, defaults to 120
:timeout => sets all three timeouts to the same value, defaults to 120

Calling Jamf::DB_CNX.connect again will re-use any values not provided. but will create a new connection.

Constants

DEFAULT_DB_NAME

The name of the JSS database on the mysql server

DFT_CHARSET

The default encoding in the tables - JAMF wisely uses UTF-8

DFT_PORT

the default MySQL port

DFT_SOCKET
DFT_TIMEOUT

give the connection a 60 second timeout, for really slow net connections (like… from airplanes)

SQL_DATE_FORMAT

the strftime format for reading/writing dates in the db

Attributes

connect_timeout[R]
connected[R]
connected?[R]
db_name[R]
port[R]
read_timeout[R]
server[R]
socket[R]
user[R]
write_timeout[R]

Public Class Methods

new() click to toggle source
    # File lib/jamf/db_connection.rb
 99 def initialize
100   @mysql = Mysql.init
101   @connected = false
102 end

Public Instance Methods

connect(**args) click to toggle source

Connect to the JSS MySQL database.

@param args the keyed arguments for connection.

@option args :server Required, the hostname of the JSS API server

@option args :port the port number to connect with, defaults to the default Mysql TCP port

@option args :socket when the server is ‘localhost’, the path to the connection socket.

@option args :db_name the name of the database to use, defaults to ‘jamfsoftware’

@option args :user Required, the mysql user to connect as

@option args :pw Required, the password for that user, or :prompt, or :stdin

If :prompt, the user is promted on the commandline to enter the password for the :user.
If :stdin#, the password is read from a line of std in represented by the digit at #,
so :stdin3 reads the passwd from the third line of standard input. defaults to line 2,
if no digit is supplied. see {JSS.stdin}

@option args :connect_timeout the number of seconds to wait for an initial response, defaults to 120

@option args :read_timeout the number of seconds before read-request times out, defaults to 120

@option args :write_timeout the number of seconds before write-request times out, defaults to 120

@option args :timeout used for any of the timeouts that aren’t explicitly set.

@return [true] the connection was successfully made.

    # File lib/jamf/db_connection.rb
135 def connect(**args)
136   begin
137     disconnect if @connected
138   rescue Mysql::ClientError::ServerGoneError
139     @connected = false
140   end
141 
142   # server might come frome several places
143   # if not given in the args, use #hostname to figure out
144   # which
145   @server = args[:server] ? args[:server] : hostname
146 
147   # settings from config if they aren't in the args
148   args[:port] ||= Jamf.config.db_server_port ? Jamf.config.db_server_port : Mysql::MYSQL_TCP_PORT
149   args[:socket] ||= Jamf.config.db_server_socket ? Jamf.config.db_server_socket : DFT_SOCKET
150   args[:db_name] ||= Jamf.config.db_name ? Jamf.config.db_name : DEFAULT_DB_NAME
151   args[:user] ||= Jamf.config.db_username
152   args[:connect_timeout] ||= Jamf.config.db_connect_timeout
153   args[:read_timeout] ||= Jamf.config.db_read_timeout
154   args[:write_timeout] ||= Jamf.config.db_write_timeout
155   args[:charset] ||= DFT_CHARSET
156 
157   ### if one timeout was given, use it for all three
158   args[:connect_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT
159   args[:read_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT
160   args[:write_timeout] ||= args[:timeout] ? args[:timeout] : DFT_TIMEOUT
161 
162   @port = args[:port]
163   @socket = args[:socket]
164   @mysql_name = args[:db_name]
165   @user = args[:user]
166   @connect_timeout = args[:connect_timeout]
167   @read_timeout = args[:read_timeout]
168   @write_timeout = args[:write_timeout]
169 
170   # make sure we have a user, pw, server
171   raise Jamf::MissingDataError, 'No MySQL user specified, or defined in configuration.' unless args[:user]
172   raise Jamf::MissingDataError, "Missing :pw (or :prompt/:stdin) for user '#{@user}'" unless args[:pw]
173   raise Jamf::MissingDataError, 'No MySQL Server hostname specified, or listed in configuration.' unless @server
174 
175   @pw = if args[:pw] == :prompt
176           JSS.prompt_for_password "Enter the password for the MySQL user #{@user}@#{@server}:"
177         elsif args[:pw].is_a?(Symbol) && args[:pw].to_s.start_with?('stdin')
178           args[:pw].to_s =~ /^stdin(\d+)$/
179           line = Regexp.last_match(1)
180           line ||= 2
181           JSS.stdin line
182         else
183           args[:pw]
184         end
185 
186   @mysql = Mysql.init
187 
188   @mysql.options Mysql::OPT_CONNECT_TIMEOUT, @connect_timeout
189   @mysql.options Mysql::OPT_READ_TIMEOUT, @read_timeout
190   @mysql.options Mysql::OPT_WRITE_TIMEOUT, @write_timeout
191   @mysql.charset = args[:charset]
192   @mysql.connect @server, @user, @pw, @mysql_name, @port, @socket
193 
194   @connected = true
195 
196   @server
197 rescue Mysql::ServerError::NotSupportedAuthMode => e
198   raise Mysql::ServerError::AccessDeniedError, "Probable unknown MySQL user '#{@user}'. Original error was 'Mysql::ServerError::NotSupportedAuthMode: #{e}' which is sometimes raised when the user does not exist on the server."
199 end
db() click to toggle source

@return [Mysql] The mysql database connection itself

    # File lib/jamf/db_connection.rb
204 def db
205   raise Jamf::InvalidConnectionError, 'No database connection. Please use Jamf::DB_CNX.connect' unless Jamf::DB_CNX.connected?
206   @mysql
207 end
disconnect() click to toggle source

close the connection to the database it’ll have to be re-connected before using again

    # File lib/jamf/db_connection.rb
213 def disconnect
214   @mysql.close! if @mysql.protocol
215   @server = nil
216   @port = nil
217   @socket = nil
218   @user = nil
219   @connection_timeout = DFT_TIMEOUT
220   @read_timeout = DFT_TIMEOUT
221   @write_timeout = DFT_TIMEOUT
222   @connected = false
223   nil
224 end
hostname() click to toggle source

The server to which we are connected, or will try connecting to if none is specified with the call to connect

@return [String] the hostname of the server

    # File lib/jamf/db_connection.rb
263 def hostname
264   # return it if already set
265   return @server if @server
266   # otherwise, from the config
267   srvr = Jamf.config.db_server_name
268   # otherwise, assume its on the JSS server to which this client talks
269   srvr ||= Jamf::Client.jss_server
270   srvr
271 end
valid_server?(server, port = DFT_PORT) click to toggle source

Test that a given hostname is a MySQL server

@param server The hostname to test

@return [Boolean] does the server host a MySQL server?

    # File lib/jamf/db_connection.rb
232 def valid_server?(server, port = DFT_PORT)
233   mysql = Mysql.init
234   mysql.options Mysql::OPT_CONNECT_TIMEOUT, 60
235   mysql.charset = DFT_CHARSET
236 
237   begin
238     # this connection should get an access denied error if there is
239     # a mysql server there. I'm assuming no one will use this username
240     # and pw for anything real
241     # Also with newer versions of mysql, a Mysql instance that has
242     # never authenticated will raise Mysql::ServerError::NotSupportedAuthMode
243     # rather than Mysql::ServerError::AccessDeniedError, until a
244     # successful connection is made. After that, re-connecting will
245     # raise AccessDeniedError when credentials are invalid.
246 
247     mysql.connect server, 'notArealUser', "definatelyNotA#{$PROCESS_ID}password", 'not_a_db', port
248 
249   rescue Mysql::ServerError::AccessDeniedError, Mysql::ServerError::NotSupportedAuthMode
250     return true
251   rescue
252     return false
253   end
254   false
255 end