require_relative 'filetype'
require 'puppet/provider/parsedfile'
Puppet::Type.type(:cron).provide(:crontab, parent: Puppet::Provider::ParsedFile, default_target: ENV['USER'] || 'root', raise_prefetch_errors: true) do
commands crontab: 'crontab'
text_line :comment, match: %r{^\s*#}, post_parse: proc { |record|
record[:name] = Regexp.last_match(1) if record[:line] =~ %r{Puppet Name: (.+)\s*$}
}
text_line :blank, match: %r{^\s*$}
text_line :environment, match: %r{^\s*\w+\s*=}
def self.filetype
tabname = case Facter.value(:osfamily)
when 'Solaris'
:suntab
when 'AIX'
:aixtab
else
:crontab
end
Puppet::Provider::Cron::FileType.filetype(tabname)
end
self::TIME_FIELDS = [:minute, :hour, :monthday, :month, :weekday].freeze
record_line :crontab,
fields: ['time', 'command'],
match: %r{^\s*(@\w+|\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$},
absent: '*',
block_eval: :instance do
def post_parse(record)
time = record.delete(:time)
match = %r{@(\S+)}.match(time)
if match
# is there another way to access the constant?
Puppet::Type::Cron::ProviderCrontab::TIME_FIELDS.each { |f| record[f] = :absent }
record[:special] = match.captures[0]
return record
end
match = %r{(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)}.match(time)
if match
record[:special] = :absent
Puppet::Type::Cron::ProviderCrontab::TIME_FIELDS.zip(match.captures).each do |field, value|
record[field] = if value == absent
:absent
else
value.split(',')
end
end
return record
end
raise Puppet::Error, _('Line got parsed as a crontab entry but cannot be handled. Please file a bug with the contents of your crontab')
end
def pre_gen(record)
if record[:special] && record[:special] != :absent
record[:special] = "@#{record[:special]}"
end
Puppet::Type::Cron::ProviderCrontab::TIME_FIELDS.each do |field|
if (vals = record[field]) && vals.is_a?(Array)
record[field] = vals.join(',')
end
end
record
end
def to_line(record)
str = ''
record[:name] = nil if record[:unmanaged]
str = "# Puppet Name: #{record[:name]}\n" if record[:name]
if record[:environment] && record[:environment] != :absent
str += record[:environment].map { |line| "#{line}\n" }.join('')
end
fields = if record[:special] && record[:special] != :absent
[:special, :command]
else
Puppet::Type::Cron::ProviderCrontab::TIME_FIELDS + [:command]
end
str += record.values_at(*fields).map { |field|
if field.nil? || field == :absent
absent
else
field
end
}.join(joiner)
str
end
end
def create
if resource.should(:command)
super
else
resource.err _('no command specified, cannot create')
end
end
# Look up a resource with a given name whose user matches a record target
#
# @api private
#
# @note This overrides the ParsedFile method for finding resources by name,
# so that only records for a given user are matched to resources of the
# same user so that orphaned records in other crontabs don't get falsely
# matched (#2251)
#
# @param [Hash<Symbol, Object>] record
# @param [Array<Puppet::Resource>] resources
#
# @return [Puppet::Resource, nil] The resource if found, else nil
def self.resource_for_record(record, resources)
resource = super
target = resource[:target] || resource[:user] if resource
return resource if record[:target] == target
end
# Return the header placed at the top of each generated file, warning
# users that modifying this file manually is probably a bad idea.
def self.header
%(# HEADER: This file was autogenerated at #{Time.now} by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.\n)
end
# Regex for finding one vixie cron header.
def self.native_header_regex
%r{# DO NOT EDIT THIS FILE.*?Cron version.*?vixie.*?\n}m
end
# If a vixie cron header is found, it should be dropped, cron will insert
# a new one in any case, so we need to avoid duplicates.
def self.drop_native_header
true
end
# See if we can match the record against an existing cron job.
def self.match(record, resources)
# if the record is named, do not even bother (#19876)
# except the resource name was implicitly generated (#3220)
return false if record[:name] && !record[:unmanaged]
resources.each do |_name, resource|
# Match the command first, since it's the most important one.
next unless record[:target] == resource[:target]
next unless record[:command] == resource.value(:command)
# Now check the time fields
compare_fields = self::TIME_FIELDS + [:special]
matched = true
compare_fields.each do |field|
# If the resource does not manage a property (say monthday) it should
# always match. If it is the other way around (e.g. resource defines
# a should value for :special but the record does not have it, we do
# not match
next unless resource[field]
unless record.include?(field)
matched = false
break
end
if (record_value = record[field]) && (resource_value = resource.value(field))
# The record translates '*' into absent in the post_parse hook and
# the resource type does exactly the opposite (alias :absent to *)
next if resource_value == '*' && record_value == :absent
next if resource_value == record_value
end
matched = false
break
end
return resource if matched
end
false
end
@name_index = 0
# Collapse name and env records.
def self.prefetch_hook(records)
name = nil
envs = nil
result = []
records.each do |record|
case record[:record_type]
when :comment
if record[:name]
name = record[:name]
record[:skip] = true
# Start collecting env values
envs = []
end
when :environment
# If we're collecting env values (meaning we're in a named cronjob),
# store the line and skip the record.
if envs
envs << record[:line]
record[:skip] = true
end
when :blank # rubocop: disable Lint/EmptyWhen
# nothing
else
if name
record[:name] = name
name = nil
else
cmd_string = record[:command].gsub(%r{\s+}, '_')
index = (@name_index += 1)
record[:name] = "unmanaged:#{cmd_string}-#{index}"
record[:unmanaged] = true
end
if envs.nil? || envs.empty?
record[:environment] = :absent
else
# Collect all of the environment lines, and mark the records to be skipped,
# since their data is included in our crontab record.
record[:environment] = envs
# And turn off env collection again
envs = nil
end
end
result << record unless record[:skip]
end
result
end
def self.to_file(records)
text = super
# Apparently Freebsd will "helpfully" add a new TZ line to every
# single cron line, but not in all cases (e.g., it doesn't do it
# on my machine). This is my attempt to fix it so the TZ lines don't
# multiply.
if text =~ %r{(^TZ=.+\n)}
tz = Regexp.last_match(1)
text.sub!(tz, '')
text = tz + text
end
text
end
def user=(user)
# we have to mark the target as modified first, to make sure that if
# we move a cronjob from userA to userB, userA's crontab will also
# be rewritten
mark_target_modified
@property_hash[:user] = user
@property_hash[:target] = user
end
def user
@property_hash[:user] || @property_hash[:target]
end
CRONTAB_DIR = case Facter.value('osfamily')
when 'Debian', 'HP-UX', 'Solaris'
'/var/spool/cron/crontabs'
when %r{BSD}
'/var/cron/tabs'
when 'Darwin'
'/usr/lib/cron/tabs/'
else
'/var/spool/cron'
end
# Return the directory holding crontab files stored on the local system.
#
# @api private
def self.crontab_dir
CRONTAB_DIR
end
# Yield the names of all crontab files stored on the local system.
#
# @note Ignores files that are not writable for the puppet process and hidden
# files that start with .keep
#
# @api private
def self.enumerate_crontabs
Puppet.debug "looking for crontabs in #{CRONTAB_DIR}"
return unless File.readable?(CRONTAB_DIR)
Dir.foreach(CRONTAB_DIR) do |file|
path = File.join(CRONTAB_DIR, file)
# Gentoo creates .keep_PACKAGE-SLOT files to make sure the directory is not
# removed
yield(file) if File.file?(path) && File.writable?(path) && !file.start_with?('.keep_')
end
end
# Include all plausible crontab files on the system
# in the list of targets (#11383 / PUP-1381)
def self.targets(resources = nil)
targets = super(resources)
enumerate_crontabs do |target|
targets << target
end
targets.uniq
end
end
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]